Connettore Javascript
Negli applicativi di frontend, il collegamento all'API viene realizzato utilizzando una libreria javascript che
incorpora la logica necessarie e previene la necessità di dover definire metodi complicati per il recupero dei dati.
La libreria, disponibile su npm come @discoverycms/connector
, richiede solamente axios come dipendenza ed espone
un insieme di metodi che possono essere utilizzati in qualunque applicazione JS, ed alcuni hook per applicazioni basate
su ReactJS.
Setup
Come prima cosa è necessario installare la libreria con npm:
npm i @discoverycms/connector --save
Completata l'installazione, è ora possibile inizializzare il connettore.
Per fare ciò è necessario richiamare il metodo setupDiscoveryCms()
come segue:
import ComponentRenderer from 'some-path';
setupDiscoveryCms({
apiRoot: 'https://some_url/API/v1/',
apiToken: '123456789',
propertyTitle: 'mywebsite',
components: {
ComponentTypeName: ComponentRenderer
},
enableConnectorScript: true,
disableCache: true
})
Al metodo viene passato un object di opzioni:
apiRoot
: (required) root della cloud API;apiToken
: (required) token utilizzato di default per recuperare contenuti dalla CloudAPI;propertyTitle
: il nome che è stato assegnato alla property sul CMS alla creazione;components
: oggetto in cui è possibile associare un componente JSX a una chiave, definita lato Discovery;enableConnectorScript
: true|false, abilita il connettore grafico per il collegamento tra i componenti ed il page-editor;disableCache
: true|false, disabilita sia il caching di Cloudfront che quello della lambda.
Infine, è necessario copiare il file discovery-cms-connector.js
dentro al proprio progetto, nella cartella public
, prendendolo da
node_modules/@discoverycms/connector
.
Al fine di rendere il connettore disponibile nell'intera applicazione e per evitare di dover replicare questa
inizializzazione in più punti, é conveniente richiamare il metodo in un file js che viene caricato ad ogni richiesta,
come ad esempio il file App.js
di un'applicazione React.
Metodi per Recuperare Singoli Contenuti
Dopo aver inizializzato il connettore, è possibile recuperarlo utilizzando il metodo getDiscoveryCms()
.
Il metodo restituisce un oggetto di classe DiscoveryCms
che espone vari metodi.
Tra questi, i principali sono:
getPage(slug: string, options: DiscoveryRequestOptions)
: recupera una pagina dall'API utilizzando il suo slug;getPageById(discoveryId: string, options: DiscoveryRequestOptions)
: recupera una pagina dall'API utilizzando il suo identificativo;getContent(slug: string, options: DiscoveryRequestOptions)
: recupera un contenuto strutturato dall'API utilizzando il suo slug;getContentById(discoveryId: string, options: DiscoveryRequestOptions)
: recupera un contento strutturato dall'API utilizzando il suo identificativo;getAsset(discoveryId: string, options: DiscoveryRequestOptions)
: permette di recuperare un asset digitale utilizzando il suo identificativo. Vedere Filtrare Asset Digitali.
Questi metodi sono incapsulati nei seguenti React hooks:
useDiscoveryContent(slug: string, options: DiscoveryRequestOptions, errorCallback: (error) => any)
: incapsula il metodogetContent
, prima descritto, in una useEffect, provvedendo a ricaricare il componente quando la chiamata all'API restituisce i dati. In caso di errore nella chiamata all'API, e' possibile passare una callback per definire il comportamento;useDiscoveryContentById(id: string, options: DiscoveryRequestOptions, errorCallback: (error) => any)
: comeuseDiscoveryContent
ma usa l'id del Content.useDiscoveryPage(slug: string, options: DiscoveryRequestOptions, errorCallback: (error) => any)
: hook che incapsula il metodogetPage
, prima descritto, in una useEffect, provvedendo a ricaricare il componente quando la chiamata all'API restituisce i dati. In caso di errore nella chiamata all'API, e' possibile passare una callback per definire il comportamento;useDiscoveryPageById(id: string, options: DiscoveryRequestOptions, errorCallback: (error) => any)
: comeuseDiscoveryPage
ma usa l'id della Page.useDiscoveryAsset(id: string, options: DiscoveryRequestOptions, errorCallback: (error) => any)
: incapsula il metodogetAsset
, prima descritto, in una useEffect, provvedendo a ricaricare il componente quando la chiamata all'API restituisce i dati. In caso di errore nella chiamata all'API, è possibile passare una callback per definire il comportamento;usePalette()
: restituisce la palette della pagina corrente, se impostata. Come fallback, prova a prendere la palette dalla pagina di layout associata alla pagina corrente.usePageData()
: restituisce l'intero oggetto relativo al JSON che la Content API fornisce per questa pagina.useLayoutComponentData(id: string)
: per le pagine che hanno una layout page associata, restituisce il componente della layout page il cui ID corrisponde a quello passato. Solo per utilizzo avanzato.
L'utilizzo del Context
è fortemente consigliato all'interno di applicazioni React. Questo infatti abilita due
ulteriori classi e un ulteriore hook:
DiscoveryComponents
: recupera il contesto definito nel connettore e crea un'istanza di ogni oggetto presente nell'arraycomponents
. E' quindi possibile inizializzare il contesto nella pagina, dopo aver recuperato i dati, in modo tale da poterli propagare ai propri componenti;DiscoveryComponent
: utilizzando la mappa dei componenti definita in fase di setup, crea un'istanza di un componente React partendo da un semplice oggetto Javascript nel quale devono essere definiti due attributi:- _id
- type: se non è stato definito nessun componente per type, un messaggio di errore viene mostrato al posto del componente.
Se invece un oggetto React è stato definito per type, l'oggetto viene creato passandogli il parametro
componentId
e viene fatto il dispatch dell'eventocreate-discovery-component
;
useComponentData(id: string)
: recupera i dati di un componente dalContext
tramite il suo uuid;usePageData()
: recupera tutti i dati della pagina corrente. L'oggetto restituito contiene le sezionidetails
(settings della pagina),components
(albero dei componenti della pagina) e, opzionalmente,layout
(dati dell'eventuale layout di pagina).
Metodi per Recuperare Contenuti Multipli
Di seguito i metodi con cui è possibile interrogare il repository per ottenere elenchi di risultati:
getContents(options: DiscoveryContentsRequestOptions)
: recupera un insieme di contenuti strutturati applicando i criteri forniti nel parametrooptions
; per dettagli, vedere sezione Metodo getContents();getAssets(options: DiscoveryContentsRequestOptions)
: recupera un insieme di asset digitali applicando i criteri forniti nel parametrooptions
; per dettagli, vedere sezione Metodo getContents() e Filtrare Assets Digitali;getFiltersFromQueryParams(queryParams: any)
: permette di trasformare i query params recuperati per una richiesta in un oggettofilters
che può essere utilizzato nelleoptions
di tipo DiscoveryRequestOptions per filtrare i risultati ottenuti. Il metodo filtra i query params forniti e applica la seguente logica:- Recupera i parametri la cui chiave inizia con
filter_
. Esempio:filter_price=...
; - Verifica se è fornito un operatore. Nel caso venga fornito, deve essere specificato tra parentesi quadre
dopo il nome del campo su cui deve essere applicato il filtro. Esempio:
filter_price[bt]=...
. Se non viene fornito nessun operatore, esempio:filter_title=Title
, l'operatore di default è[eq]
; - Compone un oggetto filters contenente i criteri convertiti. Esempio:
url...?filter_title=DemoTitle&filter_price[bt]=10
viene convertito in{
title: 'DemoTitle',
'price[bt]': 10
}
- Recupera i parametri la cui chiave inizia con
Per semplificare l'utilizzo in React è disponibile il seguente hook:
useDiscoveryContents(options: DiscoveryContentsRequestOptions, errorCallback: (error) => any)
: hook che incapsula il metodogetContents
, prima descritto, in una useEffect, provvedendo ad aggiornare il DOM quando la chiamata all'API restituisce i dati. In caso di errore nella chiamata all'API, e' possibile passare una callback per definire il comportamento;useDiscoveryAssets(options: DiscoveryContentsRequestOptions, errorCallback: (error) => any)
: hook che incapsula il metodogetAssets
, prima descritto, in una useEffect, provvedendo ad aggiornare il DOM quando la chiamata all'API restituisce i dati. In caso di errore nella chiamata all'API, e' possibile passare una callback per definire il comportamento;
Altri Metodi
getToken()
: restituisce il token fornito al setup;getComponents()
: restituisce la mappa di Components definita durante il setup;getPathList(options: DiscoveryContentsRequestOptions)
: restituisce un array contenente lo slug dei content che verificano le condizioni specificate nell'oggettooptions
per realizzare la SSG.getContext()
: restituisce il contesto React utilizzato nelle pagine dinamiche.
DiscoveryRequestOptions
La classe DiscoveryRequestOptions definisce le opzioni che possono essere passate ai metodi del connettore spiegati nella sezione Recuperare Contenuti Multipli.
Dove indicato, è consigliato usare la classe DiscoveryConstants invece d'inserire i valori stringa manualmente.
Le possibili opzioni sono:
token
: token da passare all'API, al posto di quello definito durante il setup, per recuperare altre versioni dei dati, ad esempio preview o production;authToken
: (opzionale) se l'API è dietro qualche servizio di autenticazione, come ad esempio AWS Cognito, è possibile impostare questa proprietà per passare un HTTP Header "Authorization: ..." per autenticare la chiamata.children
: permette di definire la politica di espansione delle reference contenute nella entity. Accetta uno di 3 valori:- DiscoveryConstants.CHILDREN.NONE: le reference non vengono espanse e viene fornito solamente il relativo id;
- DiscoveryConstants.CHILDREN.SUMMARY: le reference vengono espanse utilizzando il payload di summary;
- DiscoveryConstants.CHILDREN.DETAILS: le reference vengono espanse utilizzando il payload di dettaglio.
fields
: definisce quali field vengono forniti quando si recuperano dei Content o una Page;key_type
: quando si recupera una page, è possibile indicare su quale field deve essere fatta la query;disable_cache
: permette di disabilitare la cache dell'API;response_type
: permette di scegliere tra due tipi di risposta:- DiscoveryConstants.RESPONSE_TYPE.SUMMARY: la risposta viene realizzata utilizzando il payload di summary;
- DiscoveryConstants.RESPONSE_TYPE.DETAILS: la risposta viene realizzata utilizzando il payload di dettaglio;
Metodo getContents()
La classe DiscoveryContentsRequestOptions definisce le opzioni che possono essere fornite durante la ricerca di Content. Oltre a quelle definite in DiscoveryRequestOptions, le opzioni sono:
type
: definisce il content type di riferimento e deve essere specificato per ogni richiesta; si applica ai private types. (in roadmap) se si vuole far riferimento ad un global type, è necessario indicareglobalType
invece ditype
;start
: indice del primo elemento restituito dall'array di risultati recuperato dall'API;limit
: numero massimo di risultati fornito dalla richiesta;sort
: definisce l'ordinamento dei risultati in base a un attributo, per esempio:title asc
otitle desc
. E' possibile utilizzare sia un field dell'API, come per esempio title se questo è nel payload di risposta, oppure (per utenti avanzati) un field Solr, se noto. IMPORTANTE: per ordinare sui titoli va utilizzato il campo specialetitle_sortable_str asc
;filters
: oggetto contenente i filtri da passare al motore di ricerca. Vedere sezione Filtrare i Contenutilast_deploy_timestamp
: permette di recuperare solo i Content che sono stati modificati dopo una certa data/ora;fq
: (avanzato) permette di passare un array di stringhe corrispondenti a filter query da utilizzare sul motore di ricerca;
Filtrare i Contenuti
Il metodo getContents()
accetta una serie di filtri che permettono di determinare quali contenuti devono essere recuperati.
Ad esempio, se si vogliono recuperare dei prodotti, è possibile filtrare solo i prodotti di una determinata categoria.
I filtri vengono passati tramite l'opzione filters
di DiscoveryContentsRequestOptions.
Ogni attributo dell'oggetto deve avere la seguente forma fieldName[operator]: fieldValue
. L'operatore è opzionale: se non viene
indicato, viene utilizzato 'uguale' come default.
Vediamo un esempio in un contesto React in cui andiamo ad usare l'hook useDiscoveryContents()
al posto di getContents()
:
const contents = useDiscoveryContents({
type: 'Products',
children: 'details',
filters: {
title: 'DemoTitle',
'price[bt]': 10
},
sort: "title asc",
start: 0,
limit: 10 // default is 10, max is 100
})
Gli operatori disponibili sono:
eq
(equals, default), eg.title: 'Demo'
or'title[eq]': 'Demo'
neq
(not equals)in
(in range), eg.'price[in]': '10..20'
bt
(bigger than), eg.'price[bt]': 10
lt
(less than), eg.'price[lt]': 10
bte
(bigger or equals than), eg.'price[bte]': 10
lte
(less or equals than), eg.'price[lte]': 10
Il nome del field su cui deve essere applicato il filtro può essere o uno dei field restituiti nel payload oppure, se noto, il nome di un field solr, così come per il sort.
Quando si cercano campi di tipo 'Datetime;, è necessario usare il seguente formato:
yyyy-mm-ddThh:mm:ss.dddZ
, eg. 2022-09-02T09:34:15.000Z
.
Quando si cercano field di tipo 'Taxonomy', si faccia riferimento alla sezione Tassonomie
(*in roadmap) E' possibile cercare su qualsiasi campo testo usando il campo virtuale 'text' come in questo esempio:
{
filters : {
'text': 'blade runner',
}
}
Con il campo speciale text_lang
si cerca, invece, in tutti i campi di testo multilingua.
Filtrare Asset Digitali
Le possibilità di utilizzare le API per gli asset digitali sono vincolate alla licenza Discovery DAM, attivabile separatamente
Analogamente ai contenuti, sono disponibili gli hook useDiscoveryAssets({options})
e useDiscoveryAsset(assetID, {options})
.
Esempio:
const assets = useDiscoveryAssets({
type: 'Image', // or 'Video', 'Document', 'Slideshow', 'Audio', 'Binary'
filters: {
// same as useDiscoveryContents()
},
sort: ...,
start: 0,
limit: 25, // default is 10, max is 100
});
const imageAsset = useDiscoveryAsset('497839ef-3645-4f6f-944d-b9ffed02157c', {});
Tali metodi incapsulano le rispettive funzioni getAssets(options: DiscoveryContentsRequestOption)
e
getAsset(id: string, options: DiscoveryRequestOptions)
che permettono di recuperare asset digitali e
sfruttano gli stessi parametri dei metodi getContents e getContent
.
Componenti Annidati
Il connettore prevede la possibilità di avere componenti annidati all'interno di altri componenti. Per fare il rendering
di questi componenti è sufficiente usare la classe DiscoveryComponent
.
Supponiamo ad esempio di avere un componente Grid
, al primo livello di una pagina, all'interno del quale abbiamo altri
componenti.
Avendo inizializzato il nostro Context
, l'informazione di tutti i componenti sarà già disponibile nel contesto.
Conseguentemente il nostro componente Grid sarà creato usando DiscoveryComponents
. Se al suo interno i componenti figli
sono registrati con l'attributo children
, è sufficiente recuperare i dati del componente Grid
con useComponentData
e successivamente iterare sull'attributo children
, chiamando per ogni elemento dell'array DiscoveryComponent
. Esempio:
// Page
import {DiscoveryContext} from '@discoverycms/connector';
export default function Page() {
const data = ...; // i dati vengono recuperati in base al tipo di applicazione
return (
<DiscoveryContext.Provider value={data}>
<DiscoveryComponents />
</DiscoveryContext.Provider>
)
}
import {DiscoveryComponent, useComponentData} from '@discoverycms/connector';
export function Grid({componentId}) {
const data = useComponentData(componentId);
const nestedComponents = data.children ?? [];
return(
<div data-discovery-id={componentId}>
{nestedComponents.map((child) => DiscoveryComponent(child))}
</div>
)
}
export function Child({componentId}) {
const data = useComponentData(componentId);
return (
<div data-discovery-id={componentId}>
...
</div>
)
}
E' possibile notare come nel primo tag HTML di ogni componente venga usato l'attributo data-discovery-id. Tale attributo è necessario per realizzare il collegamento con il page-editor lato Discovery, in modo tale che cliccando sul componente React venga aperta la relativa sezione di gestione dei dati. L'attributo non deve essere necessariamente impostato nel primo tag HTML e può essere anche evitato, ma così facendo, sarà necessario navigare l'alberatura sul backend fino ad arrivare al componente per modificare i suoi dati.