|
| 1 | +const chalk = require('chalk') |
| 2 | +const StoryblokClient = require('storyblok-js-client') |
| 3 | + |
| 4 | +class SyncDatasources { |
| 5 | + /** |
| 6 | + * @param {{ sourceSpaceId: string, targetSpaceId: string, oauthToken: string }} options |
| 7 | + */ |
| 8 | + constructor (options) { |
| 9 | + this.targetDatasources = [] |
| 10 | + this.sourceDatasources = [] |
| 11 | + this.sourceSpaceId = options.sourceSpaceId |
| 12 | + this.targetSpaceId = options.targetSpaceId |
| 13 | + this.oauthToken = options.oauthToken |
| 14 | + this.client = new StoryblokClient({ |
| 15 | + oauthToken: options.oauthToken |
| 16 | + }) |
| 17 | + } |
| 18 | + |
| 19 | + async sync () { |
| 20 | + try { |
| 21 | + this.targetDatasources = await this.client.getAll(`spaces/${this.targetSpaceId}/datasources`) |
| 22 | + this.sourceDatasources = await this.client.getAll(`spaces/${this.sourceSpaceId}/datasources`) |
| 23 | + |
| 24 | + console.log( |
| 25 | + `${chalk.blue('-')} In source space #${this.sourceSpaceId}: ` |
| 26 | + ) |
| 27 | + console.log(` - ${this.sourceDatasources.length} datasources`) |
| 28 | + |
| 29 | + console.log( |
| 30 | + `${chalk.blue('-')} In target space #${this.targetSpaceId}: ` |
| 31 | + ) |
| 32 | + console.log(` - ${this.targetDatasources.length} datasources`) |
| 33 | + } catch (err) { |
| 34 | + console.error(`An error ocurred when loading the datasources: ${err.message}`) |
| 35 | + |
| 36 | + return Promise.reject(err) |
| 37 | + } |
| 38 | + |
| 39 | + console.log(chalk.green('-') + ' Syncing datasources...') |
| 40 | + await this.addDatasources() |
| 41 | + await this.updateDatasources() |
| 42 | + } |
| 43 | + |
| 44 | + async getDatasourceEntries (spaceId, datasourceId) { |
| 45 | + try { |
| 46 | + const entriesFirstPage = await this.client.get(`spaces/${spaceId}/datasource_entries/?datasource_id=${datasourceId}`) |
| 47 | + const entriesRequets = [] |
| 48 | + for (let i = 1; i < Math.ceil(entriesFirstPage.total / 25); i++) { |
| 49 | + entriesRequets.push(this.client.get(`spaces/${spaceId}/datasource_entries/?datasource_id=${datasourceId}`, { page: i })) |
| 50 | + } |
| 51 | + return entriesFirstPage.data.datasource_entries.concat((await Promise.all(entriesRequets)).map(r => r.data.datasource_entries)) |
| 52 | + } catch (err) { |
| 53 | + console.error(`An error ocurred when loading the entries of the datasource #${datasourceId}: ${err.message}`) |
| 54 | + |
| 55 | + return Promise.reject(err) |
| 56 | + } |
| 57 | + } |
| 58 | + |
| 59 | + async addDatasourceEntry (entry, datasourceId) { |
| 60 | + try { |
| 61 | + return this.client.post(`spaces/${this.targetSpaceId}/datasource_entries/`, { |
| 62 | + datasource_entry: { |
| 63 | + name: entry.name, |
| 64 | + value: entry.value, |
| 65 | + datasource_id: datasourceId |
| 66 | + } |
| 67 | + }) |
| 68 | + } catch (err) { |
| 69 | + console.error(`An error ocurred when creating the datasource entry ${entry.name}: ${err.message}`) |
| 70 | + |
| 71 | + return Promise.reject(err) |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + async updateDatasourceEntry (entry, newData, datasourceId) { |
| 76 | + try { |
| 77 | + return this.client.put(`spaces/${this.targetSpaceId}/datasource_entries/${entry.id}`, { |
| 78 | + datasource_entry: { |
| 79 | + name: newData.name, |
| 80 | + value: newData.value, |
| 81 | + datasource_id: datasourceId |
| 82 | + } |
| 83 | + }) |
| 84 | + } catch (err) { |
| 85 | + console.error(`An error ocurred when updating the datasource entry ${entry.name}: ${err.message}`) |
| 86 | + |
| 87 | + return Promise.reject(err) |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + async syncDatasourceEntries (sourceId, targetId) { |
| 92 | + try { |
| 93 | + const sourceEntries = await this.getDatasourceEntries(this.sourceSpaceId, sourceId) |
| 94 | + const targetEntries = await this.getDatasourceEntries(this.targetSpaceId, targetId) |
| 95 | + const updateEntries = targetEntries.filter(e => sourceEntries.map(se => se.name).includes(e.name)) |
| 96 | + const addEntries = sourceEntries.filter(e => !targetEntries.map(te => te.name).includes(e.name)) |
| 97 | + |
| 98 | + /* Update entries */ |
| 99 | + const entriesUpdateRequests = [] |
| 100 | + for (let j = 0; j < updateEntries.length; j++) { |
| 101 | + const sourceEntry = sourceEntries.find(d => d.name === updateEntries[j].name) |
| 102 | + entriesUpdateRequests.push(this.updateDatasourceEntry(updateEntries[j], sourceEntry, targetId)) |
| 103 | + } |
| 104 | + await Promise.all(entriesUpdateRequests) |
| 105 | + |
| 106 | + /* Add entries */ |
| 107 | + const entriesCreationRequests = [] |
| 108 | + for (let j = 0; j < addEntries.length; j++) { |
| 109 | + entriesCreationRequests.push(this.addDatasourceEntry(addEntries[j], targetId)) |
| 110 | + } |
| 111 | + await Promise.all(entriesCreationRequests) |
| 112 | + } catch (err) { |
| 113 | + console.error(`An error ocurred when syncing the datasource entries: ${err.message}`) |
| 114 | + |
| 115 | + return Promise.reject(err) |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + async addDatasources () { |
| 120 | + const datasourcesToAdd = this.sourceDatasources.filter(d => !this.targetDatasources.map(td => td.slug).includes(d.slug)) |
| 121 | + if (datasourcesToAdd.length) { |
| 122 | + console.log( |
| 123 | + `${chalk.green('-')} Adding new datasources to target space #${this.targetSpaceId}...` |
| 124 | + ) |
| 125 | + } |
| 126 | + |
| 127 | + for (let i = 0; i < datasourcesToAdd.length; i++) { |
| 128 | + try { |
| 129 | + /* Create the datasource */ |
| 130 | + const newDatasource = await this.client.post(`spaces/${this.targetSpaceId}/datasources`, { |
| 131 | + name: datasourcesToAdd[i].name, |
| 132 | + slug: datasourcesToAdd[i].slug |
| 133 | + }) |
| 134 | + |
| 135 | + await this.syncDatasourceEntries(datasourcesToAdd[i].id, newDatasource.data.datasource.id) |
| 136 | + console.log(chalk.green('✓') + ' Created datasource ' + datasourcesToAdd[i].name) |
| 137 | + } catch (err) { |
| 138 | + console.error( |
| 139 | + `${chalk.red('X')} Datasource ${datasourcesToAdd[i].name} creation failed: ${err.message}` |
| 140 | + ) |
| 141 | + } |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + async updateDatasources () { |
| 146 | + const datasourcesToUpdate = this.targetDatasources.filter(d => this.sourceDatasources.map(sd => sd.slug).includes(d.slug)) |
| 147 | + if (datasourcesToUpdate.length) { |
| 148 | + console.log( |
| 149 | + `${chalk.green('-')} Updating datasources In target space #${this.targetSpaceId}...` |
| 150 | + ) |
| 151 | + } |
| 152 | + |
| 153 | + for (let i = 0; i < datasourcesToUpdate.length; i++) { |
| 154 | + try { |
| 155 | + /* Update the datasource */ |
| 156 | + const sourceDatasource = this.sourceDatasources.find(d => d.slug === datasourcesToUpdate[i].slug) |
| 157 | + await this.client.put(`spaces/${this.targetSpaceId}/datasources/${datasourcesToUpdate[i].id}`, { |
| 158 | + name: sourceDatasource.name, |
| 159 | + slug: sourceDatasource.slug |
| 160 | + }) |
| 161 | + |
| 162 | + await this.syncDatasourceEntries(sourceDatasource.id, datasourcesToUpdate[i].id) |
| 163 | + console.log(chalk.green('✓') + ' Updated datasource ' + datasourcesToUpdate[i].name) |
| 164 | + } catch (err) { |
| 165 | + console.error( |
| 166 | + `${chalk.red('X')} Datasource ${datasourcesToUpdate[i].name} update failed: ${err.message}` |
| 167 | + ) |
| 168 | + } |
| 169 | + } |
| 170 | + } |
| 171 | +} |
| 172 | + |
| 173 | +module.exports = SyncDatasources |
0 commit comments