Vai al contenuto principaleVai al footer
17.01.22
Natalia Baeza

Come scrivere script di migrazione per modificare il content schema con DatoCMS

In questo tutorial, esamineremo come apportare modifiche al content schema in un progetto DatoCMS in tutta sicurezza, senza interferire con la produzione, assicurando la massima flessibilità per il test prima del merge.

Dato Environments

Avete creato un sito Web o un'app che utilizza DatoCMS per gestire i contenuti e tutto funziona perfettamente in produzione. Ora però dovete apportare modifiche alla struttura del contenuto. Come farlo in sicurezza, assicurandosi di non influire su ciò che è in produzione?

Oggi esamineremo insieme come utilizzare gli ambienti sandbox di DatoCMS e gli script di migrazione per ottenere proprio questo risultato. 

Ogni progetto DatoCMS ha un ambiente primario, dove vengono memorizzati tutti i dati del progetto per la produzione, inclusi modelli, record, caricamenti, impostazioni SEO, impostazioni delle lingue e del fuso orario, nonché impostazioni della dashboard di Dato, plug-in, il tema dell'interfaccia utente e la barra di navigazione dei contenuti. L'ambiente principale è dove gli editor gestiscono il contenuto del progetto.

Inoltre, un progetto può avere un numero qualsiasi di ambienti sandbox. Questi si avviano sempre come copie esatte (o fork) dell'ambiente principale e sono destinati a essere utilizzati per il test e lo sviluppo. Gli ambienti sandbox consentono agli sviluppatori di modificare la struttura del progetto senza interferire con ciò che è in produzione. Potete utilizzare gli ambienti sandbox come qualcosa di simile al lavoro con i git branches.

L'ambiente primario può essere forkato direttamente nella dashboard di DatoCMS o tramite la riga di comando.

Ecco come farlo passando dalla dashboard (torneremo a utilizzare Dato CLI più in basso):

Andate su Settings > Environments e poi cliccate su Fork.

Vi verrà chiesto di dare un nuovo nome alla copia dell'ambiente primario che si sta creando; il nuovo sandbox environment sarà identificato con quel nome. Una volta creato l'ambiente, possiamo passare da uno all'altro con un clic di un pulsante nella parte in alto a destra della dashboard:

Puoi impostare l'endpoint nel tuo codice per recuperare i dati da un ambiente sandbox identificandolo per nome. Se non viene specificato alcun nome, i dati vengono recuperati dall'ambiente primario. La documentazione di DatoCMS spiega diversi modi per farlo. Qui troverete invece le istruzioni per recuperare i dati specificamente per un progetto NextJS.

Flusso di lavoro consigliato

Gli ambienti sono particolarmente utili per modificare la struttura del contenuto senza interferire con la produzione.

Di seguito trovate il flusso di lavoro consigliato da DatoCMS:

  1. Create un nuovo ambiente sandbox eseguendo il fork dell'ambiente principale (con il pulsante fork nella dashboard o dalla riga di comando).
  2. Lavorate esclusivamente all'interno di questo ambiente, apportando modifiche con gli script di migrazione anziché dall'interfaccia utente.
  3. Quando siete pronti, attivate la modalità di manutenzione (con il pulsante dalla dashboard o dalla riga di comando). Gli editor non potranno aggiungere nuovi contenuti al progetto.
  4. Eseguite i vostri script di migrazione, pensati per creare prima una copia dell'ambiente principale aggiornata con la produzione e poi la modificano in base agli script.
  5. Distribuite una nuova versione del sito Web o app che punta alla nuova sandbox e verificate che tutto funzioni come previsto.
  6. Promuovete il nuovo ambiente sandbox a primario (con un pulsante nella dashboard o dalla riga di comando).
  7. Il vecchio ambiente primario sarà ora un normale ambiente sandbox e può fungere da backup se necessario.
  8. Disattivate la modalità di manutenzione.

Installare la CLI

La prima cosa che dobbiamo fare è installare l'interfaccia a riga di comando di DatoCMS. Se usate npm, potete usare il seguente comando:

npm install -g datocms-client

Se usate yarn, eseguite:

yarn global add datocms-client

Per eseguire le migrazioni, dobbiamo anche configurare il token API full-access, che può essere trovato nella dashboard di DatoCMS in Impostazioni > Autorizzazioni > Token API. Esistono vari modi per impostare il token, direttamente dalla riga di comando o caricando le variabili di ambiente da un file .env; le opzioni sono descritte in dettaglio qui.

 Ora possiamo utilizzare il client e tutte le sue varie funzioni. Se eseguite 

dato

sulla riga di comando potete vedere un elenco di tutti i comandi disponibili e i vari flag che possono essere passati a ciascuno di essi (nella foto qui sotto ho tagliato alcuni dei flag, ma potete vedere tutti i comandi attualmente disponibilihttps://www.datocms.com/marketplace/starters/nextjs-template-bloghttps://www.datocms.com/marketplace/starters/nextjs-template-blog):

Il progetto

In questo tutorial, lavoreremo su un semplice progetto di blog tratto da questo template per Next.js. Abbiamo modificato alcuni contenuti, ma la struttura è la stessa. Il progetto contiene blog post, record di autori, categorie e un modello di home page.

Ci concentreremo sul modello Author, che è strutturato in questo modo:

Ecco come appare il record di un singolo autore:

Creeremo due nuovi blocchi chiamati Contact e Avatar.  Il blocco Contact apparirà così:

Questo invece è il blocco Avatar:

Dopo aver creato i due blocchi, aggiungeremo un nuovo campo di contenuto modulare chiamato Profile al modello Author. Il campo Profile conterrà i record creati con i blocchi Avatar o Contact.

Ecco come apparirà il modello dell'autore alla fine del tutorial:

E questo il modo in cui il record completo di un autore apparirà:

La documentazione per scrivere gli script di migrazione sono dettagliati ed esaurienti. Per i nostri scopi, seguiremo i passaggi mostrati in questo esempio per creare un campo di contenuto modulare.

Adattati ai nostri scopi, questi sono i passaggi che dobbiamo seguire:

  1. Creare un blocco di contatti.
  2. Creare campi stringa.
  3. Aggiungere il modular content field al modello dell'autore e impostarlo per prendere il blocco che abbiamo creato.

Dopo aver eseguito i passaggi da 1 a 3, creeremo un blocco con un campo immagine e lo aggiungeremo al campo del contenuto modulare creato nel passaggio 3.

Infine, vedremo come aggiungere contenuto a un record, sia migrando il contenuto esistente sia passando direttamente il nuovo contenuto in uno script.

Creare un blocco contatto

Iniziamo creando un nuovo file di migrazione con il comando dato new migration seguito dal nome che vogliamo dare al nostro script. La prima volta che lo facciamo nel nostro progetto, verrà creata una nuova directory migrations nella root del progetto, con il nuovo file all'interno.

Il nome del file inizia con un timestamp, seguito dal nome che abbiamo passato al comando.

dato new migration 'Create contact block'

Ora possiamo andare alla directory migrations e trovare il nostro nuovo file al suo interno. Come tutti i nuovi file di migrazione, viene fornito precompilato con del codice di esempio; possiamo andare avanti ed eliminare tutto all'interno della funzione module.exports, quindi ci troveremo con questo:

'use strict';

module.exports = async (client) => {
}

Ora siamo pronti per esaminare i dettagli dello script che dobbiamo scrivere per creare un nuovo blocco. La documentazione per la content management API di DatoCMS ci mostrano esattamente come farlo, utilizzando lo stesso codice usato per creare un nuovo modello, ma passando modularBlock: true.

Quindi, per creare il nostro blocco Contact, dobbiamo aggiungerlo all'interno della module.exports   function`:

  const contactBlock = await client.itemTypes.create({
    name: 'Contact',
    apiKey: 'contact_block',
    modularBlock: true,
  });

Per eseguire la migrazione, usiamo il comando dato migrate seguito dalla flag --destination= e il nome dell'ambiente sandbox che creeremo per eseguire la nostra migrazione.

In questo tutorial, chiameremo il nostro sandbox environment Example.

dato migrate --destination=example

Nella console, possiamo vedere il fork dell'ambiente sandbox e la migrazione in esecuzione.

Fatto! Ora possiamo andare alla nostra dashboard di DatoCMS e passare all'ambiente Example appena creato. Se facciamo clic sulla Libreria dei blocchi, vedremo il blocco Contact che abbiamo appena creato.

Il nuovo blocco è attualmente vuoto, perciò dobbiamo creare i campi al suo interno.

Aggiungere un campo al blocco

Come sempre, iniziamo creando un nuovo file di migrazione.

dato new migration 'Add fields to contact block'

Per creare un nuovo campo nel blocco Contact, dobbiamo passare l'id del blocco e un oggetto con le proprietà label, api key, field type, validators e appearance alla funzione client.fields.create.

In generale, quando si creano nuovi campi, si dovrebbe andare nella documentazione e scorrere la lista fino a trovare la tipologia di campo che vogliamo creare. Là troveremo le istruzioni specifiche per i valori che dobbiamo passare per il field type, così come i validators disponibili e cosa passare per l'editor nella property appearance.

In questo caso, creeremo un campo a stringa singola chiamato "E-mail":

'use strict';

module.exports = async (client) => {
  const contactBlock = await client.itemType.find('contact_block');

  const emailField = await client.fields.create(contactBlock.id, {
    label: 'E-mail',
    apiKey: 'email',
    fieldType: 'string',
    validators: {
      required: {},
      format: {
        predefined_pattern: "email",
      },
    },
    appearance: {
      editor: 'single_line',
      parameters: {
        heading: false,
      },
      addons: [],
    },
  });
}

Ora siamo pronti a eseguire la migrazione con il comando dato migrate, ma invece della flag  --destination che crea nuovi ambienti, passeremo le flag --source  e  --inPlace per dire a Dato di lanciare la nuova migrazione nell'ambiente pre-esistente.

dato migrate --source=example --inPlace

Quando la migrazione è conclusa…

Possiamo tornare sulla dashboard per vedere il risultato finale:

Aggiungere un campo modular content nel modello Author

Ora che il nostro blocco Contacts è pronto, possiamo usarlo per aggiungere un campo di contenuto modulare nel modello Author, che accetterà blocchi contatto. 

Come al solito, iniziamo creando un file di migrazione.

dato new migration 'Add profile blocks to author model'

Per fugare qualsiasi dubbio, possiamo sempre consultare la documentazione su come creare un nuovo campo. Nell'elenco di "Available field types", se facciamo clic su modular content vedremo quanto segue:

Nella lista dei "Validators", se clicchiamo su rich_text_blocks invece vedremo:

Posto questo, siamo ora pronti per scrivere uno script che aggiunge un campo Profile al modello dell'autore. Come potete vedere di seguito, useremo la funzione client.fields.create e passeremo l'id del modello dell'autore e un oggetto con tutti i dati necessari per creare il nuovo campo di contenuto modulare.

'use strict';

module.exports = async (client) => {
  const contactBlock = await client.itemType.find('contact_block');
  const authorModel = await client.itemType.find('author');

  const profileBlockField = await client.fields.create(authorModel.id, {
    label: 'Profile',
    apiKey: 'profile_blocks',
    fieldType: 'rich_text',
    validators: {
      richTextBlocks: {
        itemTypes: [
          contactBlock.id,
        ]
      }
    },
    appearance: {
      editor: 'rich_text',
      parameters: {
        start_collapsed: true
      },
      addons: [],
    },
  });
}

Poi lanciamo la migrazione con:

dato migrate --source=example --inPlace

Dopo aver eseguito lo script, possiamo controllare il risultato nella dashboard di DatoCMS:

Il modello Author ora ha un nuovo campo di contenuto modulare che accetta blocchi Contact.

Condenseremo ora alcuni passaggi in un unico file di migrazione. Il nostro obiettivo è creare un nuovo blocco, che chiameremo Avatar, e che avrà un campo immagine. Quindi aggiorneremo il campo del profilo nel modello dell'autore in modo che accetti i blocchi Avatar oltre ai blocchi Contact.

Per prima cosa creiamo il file di migrazione:

dato new migration 'Add avatar image block to profile field'

Nel nostro script, il primo passaggio sarà per creare il nuovo blocco Avatar.

  const avatarBlock = await client.itemTypes.create({
    name: 'Avatar',
    apiKey: 'avatar_block',
    modularBlock: true,
  });

Poi dovremo aggiungere un campo single asset (di tipo image) nel nostro blocco appena creato:

  const imageField = await client.fields.create(avatarBlock.id, {
    label: 'Picture',
    apiKey: 'avatar_pic',
    fieldType: 'file',
    validators: {
      required: {},
      extension: {
        predefined_list: 'image',
      },
    },
    appearance: {
      editor: 'file',
      parameters: {},
      addons: [],
    },
  });

E infine dovremo aggiungere questo blocco ai riferimenti validi per questo campo di contenuto modulare nel modello Author.

  const profileBlockField = await client.field.find('author::profile_blocks');
  const validBlockTypes = profileBlockField.validators.richTextBlocks.itemTypes;

  client.field.update(profileBlockField.id, {
    validators: {
      richTextBlocks: {
        itemTypes: [...validBlockTypes, avatarBlock.id]
      }
    }
  });

Questo è come appare il file di migrazione completo:

'use strict';

module.exports = async (client) => {
  const avatarBlock = await client.itemTypes.create({
    name: 'Avatar',
    apiKey: 'avatar_block',
    modularBlock: true,
  });

  const imageField = await client.fields.create(avatarBlock.id, {
    label: 'Picture',
    apiKey: 'avatar_pic',
    fieldType: 'file',
    validators: {
      required: {},
      extension: {
        predefined_list: 'image',
      },
    },
    appearance: {
      editor: 'file',
      parameters: {},
      addons: [],
    },
  });

  const profileBlockField = await client.field.find('author::profile_blocks');
  const validBlockTypes = profileBlockField.validators.richTextBlocks.itemTypes;

  client.field.update(profileBlockField.id, {
    validators: {
      richTextBlocks: {
        itemTypes: [...validBlockTypes, avatarBlock.id]
      }
    }
  });
}

Ora possiamo eseguire la migrazione...

dato migrate --source=example --inPlace

E controllare il risultato finale nella dashboard di DatoCMS. Dovreste trovare il blocco Avatar nella Blocks Library.

Mentre nel modello Author dovreste trovare il campo Profile aggiornato:

Aggiungere contenuto al record

Sebbene gli script di migrazione debbano essere utilizzati principalmente per modificare la struttura del progetto, a volte è necessario migrare anche il contenuto.

Esaminiamo quindi come aggiungere contenuto a un record sia migrando il contenuto esistente da un campo all'altro sia aggiungendo direttamente nuovo contenuto.

Ma prima, come al solito, dobbiamo creare un nuovo file di migrazione.

dato new migration 'Update author record with new profile blocks content'

Mentre andiamo a scrivere il nostro script, la prima cosa da fare è controllare nella documentazione come aggiungere un blocco modulare in un record; là ci viene spiegato che il prossimo passo è quello di richiedere la funzione buildModularBlock dal client di DatoCMS. Per farlo, dobbiamo aggiungere quanto segue all'inizio del nostro file:

const { buildModularBlock } = require('datocms-client');

Quindi dobbiamo trovare il record che aggiorneremo. Per fare ciò, elencheremo tutti i record dell'autore e quindi sceglieremo quello che vogliamo; in questo caso, sceglieremo quello in cui il campo del nome è "Super Dev". Aggiorneremo quindi questo record aggiungendo due nuovi blocchi di contenuto modulari al suo campo Profile; uno sarà di tipo Avatar e conterrà un'immagine con lo stesso contenuto di quella già esistente nel campo dell'immagine dell'autore. L'altro sarà di tipo Contact e avrà un indirizzo email che passeremo direttamente nello script.

'use strict';

const { buildModularBlock } = require('datocms-client');

module.exports = async (client) => {
  // Retrieve author records
  const authorRecords = await client.items.all(
    {
       filter: {
          type: 'author'
        }
      },
    {
        allPages: true,  // otherwise we get only the first page of results
    },
  );

  // Retrieve record for Super Dev
  const superDevRecord = authorRecords.find(rec => rec.name === 'Super Dev');

  // Content in existing picture field
  const superDevPicture = superDevRecord.picture;

  // Block types that we are going to insert
  const avatarBlock = await client.itemType.find('avatar_block');
  const contactBlock = await client.itemType.find('contact_block');

  // Update record
  const createSuperDevProfile = await client.items
    .update(superDevRecord.id, {
      profileBlocks: [
        buildModularBlock({
          itemType: avatarBlock.id,
          avatarPic: superDevPicture,
        }),
        buildModularBlock({
          itemType: contactBlock.id,
          email: 'superdev@me.com',
        }),
      ]
    });
  }

Ora siamo pronti per eseguire lo script con il nostro comando ormai familiare

dato migrate --source=example --inPlace

per coi controllare i risultati nella dashboard. Questo è il record aggiornato del nostro autore chiamato "Super Dev":

Conclusione

Ora che abbiamo creato tutte le migrazioni di cui abbiamo bisogno e siamo soddisfatti del risultato, possiamo eliminare l'ambiente di esempio e seguire il flusso di lavoro per incorporare queste modifiche nella produzione.

  1. Elimina l'ambiente sandbox di esempio dalla dashboard o con il comando dato environment destroy example.
  2. Attiva la modalità di manutenzione dalla dashboard o con il comando dato maintenance on. Se qualcuno sta attualmente inserendo contenuto nel progetto, il comando fallirà. Possiamo passare la variabile  --force , e saranno forzati a chiudere il progetto fino a quando la manutenzione non sarà disattivata.
  3. Esegui nuovamente le migrazioni dalla riga di comando con dato migrate --destination=environment_name.
  4. Questo è il punto in cui dovreste impostare l'endpoint nel vostro progetto per leggere i dati dal nuovo ambiente di DatoCMS e distribuire il vostro sito web o la tua app per assicurarvi che tutto funzioni correttamente.
  5. Promuovi l'ambiente appena creato a primario dalla dashboard oppure con il comando dato environment promote environment_name.
  6. Disattiva la modalità di manutenzione dalla dashboard o con il comando dato maintenance off.

Questo è tutto! Nel caso in cui sia necessario ripristinare le modifiche, il vecchio ambiente principale sarà ancora presente tra gli ambienti sandbox, quindi potete semplicemente ripristinarlo promuovendolo.

Come abbiamo visto, tutti i comandi possono essere eseguiti dalla riga di comando, il che semplifica l'automatizzazione del processo in una pipeline CI/CD.

Apportare modifiche agli schemi dei contenuti con gli script di migrazione è semplice e versatile e vi consente di modificare lo schema dei contenuti con la massima tranquillità, sapendo che potete giocare con i sandbox senza interferire con il vostro ambiente di produzione.

Di recente abbiamo tenuto una lezione su questo argomento per il corso di HeadlessCreator su DatoCMS; se sei interessato, potete vedere l'inizio della nostra lezione su YouTube in apertura di questo articolo o registrandovi nel sito web di HeadlessCreator per vedere la lezione completa.