Passa al contenuto principale

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 metodo getContent, 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): come useDiscoveryContent ma usa l'id del Content.
  • useDiscoveryPage(slug: string, options: DiscoveryRequestOptions, errorCallback: (error) => any): hook che incapsula il metodo getPage, 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): come useDiscoveryPage ma usa l'id della Page.
  • useDiscoveryAsset(id: string, options: DiscoveryRequestOptions, errorCallback: (error) => any): incapsula il metodo getAsset, 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'array components. 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'evento create-discovery-component;
  • useComponentData(id: string): recupera i dati di un componente dal Context tramite il suo uuid;
  • usePageData(): recupera tutti i dati della pagina corrente. L'oggetto restituito contiene le sezioni details (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 parametro options; per dettagli, vedere sezione Metodo getContents();
  • getAssets(options: DiscoveryContentsRequestOptions): recupera un insieme di asset digitali applicando i criteri forniti nel parametro options; 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 oggetto filters che può essere utilizzato nelle options 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
      }

Per semplificare l'utilizzo in React è disponibile il seguente hook:

  • useDiscoveryContents(options: DiscoveryContentsRequestOptions, errorCallback: (error) => any): hook che incapsula il metodo getContents, 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 metodo getAssets, 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'oggetto options 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 indicare globalType invece di type;
  • 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 o title 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 speciale title_sortable_str asc;
  • filters: oggetto contenente i filtri da passare al motore di ricerca. Vedere sezione Filtrare i Contenuti
  • last_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.