diff --git a/packages/botonic-plugin-contentful/bin/l10n/import-from-translators.sh b/packages/botonic-plugin-contentful/bin/l10n/import-from-translators.sh index 341ffdde0c..5cf118c3d5 100755 --- a/packages/botonic-plugin-contentful/bin/l10n/import-from-translators.sh +++ b/packages/botonic-plugin-contentful/bin/l10n/import-from-translators.sh @@ -1,27 +1,32 @@ #!/bin/zsh +set -x +SPACE_ID=$1 +ENVIRONMENT=$2 +DELIVER_TOKEN=$3 +CONTENTFUL_MANAGEMENT_TOKEN=$4 +LOCALE=$5 +CSV_FILENAME=$6 +CSV_FILENAME="$( cd "$( dirname "$CSV_FILENAME" )" && pwd )/$(basename $CSV_FILENAME)" + + BIN_DIR=${0:a:h} cd "$BIN_DIR"/../.. || exit if [[ $# -lt 8 ]]; then - ../../node_modules/.bin/ts-node --files src/tools/l10n/import-csv-for-translators.ts --help + ../../node_modules/.bin/ts-node --files src/tools/l10n/import-csv-from-translators.ts --help exit 1 fi -SPACE_ID=$1 -ENVIRONMENT=$2 -DELIVER_TOKEN=$3 -CONTENTFUL_MANAGEMENT_TOKEN=$4 -LOCALE=$5 -CSV_FILENAME=$6 WRITE_MODE=$7 # DRY: parse files but do write to CM # NO_OVERWRITE: publishes the content, but fails if fields for this locale already have value # OVERWRITE: modifies previous value, but leaves it in UNPUBLISHED state +# OVERWRITE_AND_PUBLISH: overwrites previous value and publishes it (only for new spaces!) DUPLICATE_REFERENCES=$8 -../../node_modules/.bin/ts-node --files src/tools/l10n/import-csv-for-translators.ts \ +../../node_modules/.bin/ts-node --files src/tools/l10n/import-csv-from-translators.ts \ "$SPACE_ID" "$ENVIRONMENT" "$DELIVER_TOKEN" "$CONTENTFUL_MANAGEMENT_TOKEN" "$LOCALE" "$CSV_FILENAME" \ "$WRITE_MODE" "$DUPLICATE_REFERENCES" diff --git a/packages/botonic-plugin-contentful/bin/l10n/migrate-locales.sh b/packages/botonic-plugin-contentful/bin/l10n/migrate-locales.sh index f183f05522..fdade18a94 100755 --- a/packages/botonic-plugin-contentful/bin/l10n/migrate-locales.sh +++ b/packages/botonic-plugin-contentful/bin/l10n/migrate-locales.sh @@ -1,8 +1,20 @@ #!/bin/zsh +set -e # Useful to clone the contents flow from a space when the target locales are different. # It clones the specifying exported json file with the requested changes # See LocaleMigrator class +FROM_FILE=$1 +FROM_FILE="$( cd "$( dirname "$FROM_FILE" )" && pwd )/$(basename $FROM_FILE)" + +TO_FILE=$2 +TO_FILE="$( cd "$( dirname "$TO_FILE" )" && pwd )/$(basename $TO_FILE)" + +FROM_LOCALE=$3 +TO_LOCALE=$4 +REMOVE_LOCALES=$5 + + BIN_DIR=${0:a:h} cd "$BIN_DIR"/../.. || exit @@ -11,11 +23,7 @@ if [[ $# -lt 4 ]]; then exit 1 fi -FROM_FILE=$1 -TO_FILE=$2 -FROM_LOCALE=$3 -TO_LOCALE=$4 -REMOVE_LOCALES=$5 ../../node_modules/.bin/ts-node --files src/tools/l10n/locale-migrate.ts \ "$FROM_FILE" "$TO_FILE" "$FROM_LOCALE" "$TO_LOCALE" "$REMOVE_LOCALES" +echo "Change Element.image so that it does not have 1 version per locale" diff --git a/packages/botonic-plugin-contentful/src/cms/cms-dummy.ts b/packages/botonic-plugin-contentful/src/cms/cms-dummy.ts index 35818fa41f..661e09c038 100644 --- a/packages/botonic-plugin-contentful/src/cms/cms-dummy.ts +++ b/packages/botonic-plugin-contentful/src/cms/cms-dummy.ts @@ -138,7 +138,7 @@ export class DummyCMS implements CMS { } asset(id: string, context?: Context): Promise { - return Promise.resolve(new Asset(`name for ${id}`, `http://url.${id}`)) + return Promise.resolve(new Asset(id, `name for ${id}`, `http://url.${id}`)) } dateRange(id: string, context?: Context): Promise { @@ -156,4 +156,8 @@ export class DummyCMS implements CMS { contents(contentType: ContentType, context?: Context): Promise { return Promise.resolve([]) } + + assets(context?: Context): Promise { + return Promise.resolve([]) + } } diff --git a/packages/botonic-plugin-contentful/src/cms/cms-error.ts b/packages/botonic-plugin-contentful/src/cms/cms-error.ts index 97d568ff25..c7bd7c2814 100644 --- a/packages/botonic-plugin-contentful/src/cms/cms-error.ts +++ b/packages/botonic-plugin-contentful/src/cms/cms-error.ts @@ -111,15 +111,16 @@ export class ErrorReportingCMS implements CMS { .catch(this.handleError('topContents')) } - contents( - contentType: ContentType, - context?: Context | undefined - ): Promise { + contents(contentType: ContentType, context?: Context): Promise { return this.cms .contents(contentType, context) .catch(this.handleError('contents')) } + assets(context?: Context): Promise { + return this.cms.assets(context).catch(this.handleError('assets')) + } + schedule(id: string, context?: Context): Promise { return this.cms .schedule(id, context) diff --git a/packages/botonic-plugin-contentful/src/cms/cms-multilocale.ts b/packages/botonic-plugin-contentful/src/cms/cms-multilocale.ts index c492f2f3c2..0d71993050 100644 --- a/packages/botonic-plugin-contentful/src/cms/cms-multilocale.ts +++ b/packages/botonic-plugin-contentful/src/cms/cms-multilocale.ts @@ -46,6 +46,10 @@ export class MultiContextCms implements CMS { return this.cmsFromContext(context).contents(contentType, context) } + assets(context?: Context): Promise { + return this.cmsFromContext(context).assets(context) + } + contentsWithKeywords(context?: Context): Promise { return this.cmsFromContext(context).contentsWithKeywords(context) } diff --git a/packages/botonic-plugin-contentful/src/cms/cms.ts b/packages/botonic-plugin-contentful/src/cms/cms.ts index 9f99231600..170cd91d9d 100644 --- a/packages/botonic-plugin-contentful/src/cms/cms.ts +++ b/packages/botonic-plugin-contentful/src/cms/cms.ts @@ -104,6 +104,7 @@ export interface CMS { * TODO add filter by id or name */ contents(contentType: ContentType, context?: Context): Promise + assets(context?: Context): Promise /** * For contents with 'Searchable by' field (eg. {@link Queue}), it returns one result per each 'Seachable by' entry diff --git a/packages/botonic-plugin-contentful/src/cms/contents.ts b/packages/botonic-plugin-contentful/src/cms/contents.ts index 9e31dfd8e2..ab9c7d6780 100644 --- a/packages/botonic-plugin-contentful/src/cms/contents.ts +++ b/packages/botonic-plugin-contentful/src/cms/contents.ts @@ -16,6 +16,7 @@ export class Asset { * @param details depends on the type. eg the image size */ constructor( + readonly id: string, readonly name: string, readonly url: string, readonly type?: string, diff --git a/packages/botonic-plugin-contentful/src/cms/transform/cms-filter.ts b/packages/botonic-plugin-contentful/src/cms/transform/cms-filter.ts index 5c6d5d6700..1c55aeb3f3 100644 --- a/packages/botonic-plugin-contentful/src/cms/transform/cms-filter.ts +++ b/packages/botonic-plugin-contentful/src/cms/transform/cms-filter.ts @@ -124,6 +124,10 @@ export class FilteredCMS implements CMS { return this.filterContents(contents, context) } + assets(context?: Context): Promise { + return this.cms.assets(context) + } + schedule(id: string, context?: Context): Promise { return this.cms.schedule(id, context) } diff --git a/packages/botonic-plugin-contentful/src/contentful/cms-contentful.ts b/packages/botonic-plugin-contentful/src/contentful/cms-contentful.ts index 2fe51e340f..8be675bb40 100644 --- a/packages/botonic-plugin-contentful/src/contentful/cms-contentful.ts +++ b/packages/botonic-plugin-contentful/src/contentful/cms-contentful.ts @@ -1,5 +1,6 @@ import * as cms from '../cms' import { + Asset, CommonFields, Content, ContentType, @@ -220,8 +221,12 @@ export class Contentful implements cms.CMS { return this._schedule.schedule(id) } - asset(id: string): Promise { - return this._asset.asset(id) + asset(id: string, context = DEFAULT_CONTEXT): Promise { + return this._asset.asset(id, context) + } + + assets(context = DEFAULT_CONTEXT): Promise { + return this._asset.assets(context) } dateRange(id: string): Promise { diff --git a/packages/botonic-plugin-contentful/src/contentful/contents/asset.ts b/packages/botonic-plugin-contentful/src/contentful/contents/asset.ts index b51b7c9b62..8ab978a45c 100644 --- a/packages/botonic-plugin-contentful/src/contentful/contents/asset.ts +++ b/packages/botonic-plugin-contentful/src/contentful/contents/asset.ts @@ -1,16 +1,27 @@ import * as cms from '../../cms' import { ContentfulEntryUtils, DeliveryApi } from '../delivery-api' +import { Asset } from 'contentful' export class AssetDelivery { constructor(protected delivery: DeliveryApi) {} - async asset(id: string): Promise { - const asset = await this.delivery.getAsset(id) + async asset(id: string, context: cms.Context): Promise { + const asset = await this.delivery.getAsset(id, context) + return this.fromEntry(asset) + } + + private fromEntry(asset: Asset) { return new cms.Asset( + asset.sys.id, asset.fields.title, ContentfulEntryUtils.urlFromAsset(asset), asset.fields.file.contentType, asset.fields.file.details ) } + + async assets(context: cms.Context): Promise { + const assets = await this.delivery.getAssets(context) + return assets.items.map(a => this.fromEntry(a)) + } } diff --git a/packages/botonic-plugin-contentful/src/contentful/delivery-api.ts b/packages/botonic-plugin-contentful/src/contentful/delivery-api.ts index 490b754dc0..ae89523316 100644 --- a/packages/botonic-plugin-contentful/src/contentful/delivery-api.ts +++ b/packages/botonic-plugin-contentful/src/contentful/delivery-api.ts @@ -17,7 +17,9 @@ import { ReducedClientApi } from './delivery/client-api' import { ContentfulOptions } from '../plugin' export interface DeliveryApi { - getAsset(id: string, query?: any): Promise + getAsset(id: string, context: Context, query?: any): Promise + + getAssets(context: Context, query?: any): Promise getEntry( id: string, @@ -42,8 +44,19 @@ export class AdaptorDeliveryApi implements DeliveryApi { readonly options: ContentfulOptions ) {} - async getAsset(id: string, query?: any): Promise { - return this.client.getAsset(id, query) + async getAsset( + id: string, + context: Context, + query?: any + ): Promise { + return this.client.getAsset(id, this.queryFromContext(context, query)) + } + + async getAssets( + context: Context, + query?: any + ): Promise { + return this.client.getAssets(this.queryFromContext(context, query)) } async getEntry( diff --git a/packages/botonic-plugin-contentful/src/contentful/delivery/cache.ts b/packages/botonic-plugin-contentful/src/contentful/delivery/cache.ts index 0ad1a2fcf9..a7322a9692 100644 --- a/packages/botonic-plugin-contentful/src/contentful/delivery/cache.ts +++ b/packages/botonic-plugin-contentful/src/contentful/delivery/cache.ts @@ -5,6 +5,7 @@ import { ReducedClientApi } from './client-api' export class CachedClientApi implements ReducedClientApi { readonly getAsset: (id: string, query?: any) => Promise + readonly getAssets: (query?: any) => Promise readonly getEntries: (query: any) => Promise> readonly getEntry: (id: string, query?: any) => Promise> readonly getContentType: (id: string) => Promise @@ -23,6 +24,7 @@ export class CachedClientApi implements ReducedClientApi { } as memoize.Options) this.getAsset = memoize(client.getAsset, options(2)) + this.getAssets = memoize(client.getAssets, options(1)) this.getEntries = memoize(client.getEntries, options(1)) this.getEntry = memoize(client.getEntry, options(2)) this.getContentType = memoize(client.getContentType, options(1)) diff --git a/packages/botonic-plugin-contentful/src/contentful/delivery/client-api.ts b/packages/botonic-plugin-contentful/src/contentful/delivery/client-api.ts index 6c79d72262..aa31411ea7 100644 --- a/packages/botonic-plugin-contentful/src/contentful/delivery/client-api.ts +++ b/packages/botonic-plugin-contentful/src/contentful/delivery/client-api.ts @@ -2,5 +2,5 @@ import { ContentfulClientApi } from 'contentful' export type ReducedClientApi = Pick< ContentfulClientApi, - 'getAsset' | 'getEntries' | 'getEntry' | 'getContentType' + 'getAsset' | 'getAssets' | 'getEntries' | 'getEntry' | 'getContentType' > diff --git a/packages/botonic-plugin-contentful/src/contentful/ignore-fallback-decorator.ts b/packages/botonic-plugin-contentful/src/contentful/ignore-fallback-decorator.ts index 669c31fb2f..1ba12a06b5 100644 --- a/packages/botonic-plugin-contentful/src/contentful/ignore-fallback-decorator.ts +++ b/packages/botonic-plugin-contentful/src/contentful/ignore-fallback-decorator.ts @@ -7,6 +7,7 @@ import { I18nValue, VisitedField, } from './traverser' +import * as contentful from 'contentful' /** * It requests contentful to deliver all locales for each entry, and we discard all except the one in the context @@ -61,8 +62,21 @@ export class IgnoreFallbackDecorator implements DeliveryApi { ) } - getAsset(id: string, query?: any): Promise { - return this.api.getAsset(id, query) + getAsset(id: string, context: Context, query?: any): Promise { + console.warn( + 'IgnoreFallbackDecorator does not any special treatment for getAsset' + ) + return this.api.getAsset(id, context, query) + } + + async getAssets( + context: Context, + query?: any + ): Promise { + console.warn( + 'IgnoreFallbackDecorator does not any special treatment for getAssets' + ) + return this.api.getAssets(context, query) } private i18nContext(context: Context) { diff --git a/packages/botonic-plugin-contentful/src/contentful/manage/manage-contentful.ts b/packages/botonic-plugin-contentful/src/contentful/manage/manage-contentful.ts index 88d8fa8659..5723d183af 100644 --- a/packages/botonic-plugin-contentful/src/contentful/manage/manage-contentful.ts +++ b/packages/botonic-plugin-contentful/src/contentful/manage/manage-contentful.ts @@ -1,6 +1,7 @@ import { ManageCms } from '../../manage-cms/manage-cms' -import * as cms from '../../cms' +import { CmsException, ContentId } from '../../cms' import * as nlp from '../../nlp' +import { Locale } from '../../nlp' import { createClient } from 'contentful-management' // eslint-disable-next-line node/no-missing-import import { ClientAPI } from 'contentful-management/dist/typings/create-contentful-api' @@ -8,15 +9,15 @@ import { ClientAPI } from 'contentful-management/dist/typings/create-contentful- import { Environment } from 'contentful-management/dist/typings/entities/environment' // eslint-disable-next-line node/no-missing-import import { Entry } from 'contentful-management/dist/typings/entities/entry' +// eslint-disable-next-line node/no-missing-import +import { Asset } from 'contentful-management/dist/typings/entities/asset' import { ContentfulOptions } from '../../plugin' import { ManageContext } from '../../manage-cms/manage-context' -import { CmsException, ContentId } from '../../cms' import { CONTENT_FIELDS, ContentField, ContentFieldType, } from '../../manage-cms/fields' -import { Locale } from '../../nlp' export class ManageContentful implements ManageCms { readonly manage: ClientAPI @@ -55,7 +56,7 @@ export class ManageContentful implements ManageCms { return this.environment } - async updateField( + async updateField( context: ManageContext, contentId: ContentId, fieldType: ContentFieldType, @@ -67,7 +68,39 @@ export class ManageContentful implements ManageCms { oldEntry.fields[field.cmsName][context.locale] = value // we could use this.deliver.contentFromEntry & IgnoreFallbackDecorator to convert // the multilocale fields returned by update() - await this.updateEntry(context, oldEntry) + await this.writeEntry(context, oldEntry) + } + + async removeAssetFile( + context: ManageContext, + assetId: string + ): Promise { + const environment = await this.getEnvironment() + const asset = await environment.getAsset(assetId) + delete asset.fields.file[context.locale] + await this.writeAsset({ ...context, allowOverwrites: true }, asset) + } + + async copyAssetFile( + context: ManageContext, + assetId: string, + fromLocale: nlp.Locale + ): Promise { + const environment = await this.getEnvironment() + const oldAsset = await environment.getAsset(assetId) + if (!context.allowOverwrites && oldAsset.fields.file[context.locale]) { + throw new Error( + `Cannot overwrite asset '${assetId}' because it's not empty and ManageContext.allowOverwrites is false` + ) + } + const fromFile = oldAsset.fields.file[fromLocale] + if (!fromFile) { + throw Error(`Asset '${assetId}' has no file for locale ${fromLocale}`) + } + oldAsset.fields.file[context.locale] = fromFile + // we could use this.deliver.contentFromEntry & IgnoreFallbackDecorator to convert + // the multilocale fields returned by update() + await this.writeAsset(context, oldAsset) } private checkOverwrite( @@ -111,7 +144,7 @@ export class ManageContentful implements ManageCms { return field } - async copyField( + async copyField( context: ManageContext, contentId: ContentId, fieldType: ContentFieldType, @@ -130,10 +163,13 @@ export class ManageContentful implements ManageCms { return } fieldEntry[context.locale] = fieldEntry[fromLocale] - await this.updateEntry(context, oldEntry) + await this.writeEntry(context, oldEntry) } - async updateEntry(context: ManageContext, entry: Entry): Promise { + private async writeEntry( + context: ManageContext, + entry: Entry + ): Promise { if (context.dryRun) { console.log('Not updating due to dryRun mode') return @@ -143,4 +179,18 @@ export class ManageContentful implements ManageCms { await updated.publish() } } + + private async writeAsset( + context: ManageContext, + asset: Asset + ): Promise { + if (context.dryRun) { + console.log('Not updating due to dryRun mode') + return + } + const updated = await asset.update() + if (!context.preview) { + await updated.publish() + } + } } diff --git a/packages/botonic-plugin-contentful/src/manage-cms/manage-cms-error.ts b/packages/botonic-plugin-contentful/src/manage-cms/manage-cms-error.ts index d4a134b534..3fba75bad8 100644 --- a/packages/botonic-plugin-contentful/src/manage-cms/manage-cms-error.ts +++ b/packages/botonic-plugin-contentful/src/manage-cms/manage-cms-error.ts @@ -18,7 +18,7 @@ export class ErrorReportingManageCms implements ManageCms { value: any ): Promise { return this.manageCms - .updateField(context, contentId, fieldType, value) + .updateField(context, contentId, fieldType, value) .catch(this.handleError('updateField', contentId)) } @@ -30,7 +30,7 @@ export class ErrorReportingManageCms implements ManageCms { onlyIfTargetEmpty: boolean ): Promise { return this.manageCms - .copyField(context, contentId, field, fromLocale, onlyIfTargetEmpty) + .copyField(context, contentId, field, fromLocale, onlyIfTargetEmpty) .catch(this.handleError('copyField', contentId)) } @@ -53,4 +53,20 @@ export class ErrorReportingManageCms implements ManageCms { .getDefaultLocale() .catch(this.handleError('defaultLocale')) } + + copyAssetFile( + context: ManageContext, + assetId: string, + fromLocale: Locale + ): Promise { + return this.manageCms + .copyAssetFile(context, assetId, fromLocale) + .catch(this.handleError('copyAssetFile')) + } + + removeAssetFile(context: ManageContext, assetId: string): Promise { + return this.manageCms + .removeAssetFile(context, assetId) + .catch(this.handleError('removeAssetFile')) + } } diff --git a/packages/botonic-plugin-contentful/src/manage-cms/manage-cms.ts b/packages/botonic-plugin-contentful/src/manage-cms/manage-cms.ts index 3a225e1d02..51c6a717d9 100644 --- a/packages/botonic-plugin-contentful/src/manage-cms/manage-cms.ts +++ b/packages/botonic-plugin-contentful/src/manage-cms/manage-cms.ts @@ -1,9 +1,8 @@ -import * as cms from '../cms' -import { ManageContext } from './manage-context' import { ContentId } from '../cms' +import { ManageContext } from './manage-context' import * as nlp from '../nlp' -import { ContentFieldType } from './fields' import { Locale } from '../nlp' +import { ContentFieldType } from './fields' /** * Take into account that if you request a content immediately after updating it @@ -15,7 +14,7 @@ export interface ManageCms { */ getDefaultLocale(): Promise - updateField( + updateField( context: ManageContext, contentId: ContentId, fieldType: ContentFieldType, @@ -23,11 +22,19 @@ export interface ManageCms { ): Promise /** Will not fail if source does not have this field */ - copyField( + copyField( context: ManageContext, contentId: ContentId, field: ContentFieldType, fromLocale: nlp.Locale, onlyIfTargetEmpty: boolean ): Promise + + copyAssetFile( + context: ManageContext, + assetId: string, + fromLocale: nlp.Locale + ): Promise + + removeAssetFile(context: ManageContext, assetId: string): Promise } diff --git a/packages/botonic-plugin-contentful/src/tools/l10n/csv-import.ts b/packages/botonic-plugin-contentful/src/tools/l10n/csv-import.ts index ab1769528b..51395b82c1 100644 --- a/packages/botonic-plugin-contentful/src/tools/l10n/csv-import.ts +++ b/packages/botonic-plugin-contentful/src/tools/l10n/csv-import.ts @@ -132,7 +132,7 @@ export class ReferenceFieldDuplicator { readonly manageContext: ManageContext ) {} - async duplicateReferenceFields() { + async duplicateReferenceFields(): Promise { const defaultLocale = await this.manageCms.getDefaultLocale() const fields = { [ContentType.TEXT]: [ContentFieldType.BUTTONS], @@ -140,9 +140,9 @@ export class ReferenceFieldDuplicator { [ContentType.ELEMENT]: [ContentFieldType.IMAGE], } for (const contentType of Object.keys(fields)) { - console.log(`***Duplicating contents of type ${contentType}`) + console.log(`***Duplicating contents of type '${contentType}'`) for (const fieldType of (fields as any)[contentType]) { - console.log(` **Duplicating field ${contentType}`) + console.log(` **Duplicating '${contentType}' fields`) await this.duplicate( defaultLocale, contentType as ContentType, @@ -150,6 +150,28 @@ export class ReferenceFieldDuplicator { ) } } + this.warning() + } + + async duplicateAssetFiles() { + const defaultLocale = await this.manageCms.getDefaultLocale() + console.log(`***Duplicating assets`) + const assets = await this.cms.assets({ locale: defaultLocale }) + console.log(` **Duplicating ${assets.length} assets`) + for (const a of assets) { + await this.manageCms.copyAssetFile( + this.manageContext, + a.id, + defaultLocale + ) + } + this.warning() + } + + private warning() { + if (this.manageContext.preview) { + console.warn('Remember to publish the entries from contentful.com') + } } private async duplicate( @@ -162,7 +184,7 @@ export class ReferenceFieldDuplicator { locale: defaultLocale, }) for (const content of contents) { - console.log(` *Duplicating ${content.id} (${content.name})`) + //console.log(` *Duplicating ${content.id} (${content.name})`) await this.manageCms.copyField( this.manageContext, new ContentId(contentType, content.id), diff --git a/packages/botonic-plugin-contentful/src/tools/l10n/import-csv-for-translators.ts b/packages/botonic-plugin-contentful/src/tools/l10n/import-csv-from-translators.ts similarity index 91% rename from packages/botonic-plugin-contentful/src/tools/l10n/import-csv-for-translators.ts rename to packages/botonic-plugin-contentful/src/tools/l10n/import-csv-from-translators.ts index 5047999391..3670ba485d 100644 --- a/packages/botonic-plugin-contentful/src/tools/l10n/import-csv-for-translators.ts +++ b/packages/botonic-plugin-contentful/src/tools/l10n/import-csv-from-translators.ts @@ -24,6 +24,7 @@ export enum ImportType { DRY = 'DRY', //parse files but do write to CM NO_OVERWRITE = 'NO_OVERWRITE', // publishes the content, but fails if fields for this locale already have value OVERWRITE = 'OVERWRITE', // modifies previous value, but leaves it in UNPUBLISHED state + OVERWRITE_AND_PUBLISH = 'OVERWRITE_AND_PUBLISH', // overwrites previous value and publishes it (only for new spaces) } if (process.argv.length < 10 || process.argv[2] == '--help') { @@ -72,9 +73,13 @@ async function main() { locale, preview: importType == ImportType.OVERWRITE, dryRun: importType == ImportType.DRY, - allowOverwrites: importType == ImportType.OVERWRITE, + allowOverwrites: [ + ImportType.OVERWRITE, + ImportType.OVERWRITE_AND_PUBLISH, + ].includes(importType as ImportType), } + await readCsvForTranslators(manageOptions, manageContext, fileName) if (duplicateReferences) { console.log('Duplicating reference fields') await duplicateReferenceFields( @@ -87,7 +92,6 @@ async function main() { manageContext ) } - await readCsvForTranslators(manageOptions, manageContext, fileName) console.log('done') } catch (e) { console.error(e) @@ -112,6 +116,7 @@ async function duplicateReferenceFields( manageContext ) await referenceDuplicator.duplicateReferenceFields() + await referenceDuplicator.duplicateAssetFiles() } // void tells linters that we don't want to wait for promise diff --git a/packages/botonic-plugin-contentful/src/tools/l10n/locale-migrate.ts b/packages/botonic-plugin-contentful/src/tools/l10n/locale-migrate.ts index 4ab9b2214e..ab47cd04b1 100644 --- a/packages/botonic-plugin-contentful/src/tools/l10n/locale-migrate.ts +++ b/packages/botonic-plugin-contentful/src/tools/l10n/locale-migrate.ts @@ -6,6 +6,9 @@ import { SpaceExport } from '../../contentful/export/space-export' if (process.argv.length < 6 || process.argv[2] == '--help') { console.error(`Usage: fromFile toFile fromLocale toLocale [removeLocales]`) + console.error( + 'removeLocales: locales to remove, separated with commas. Eg: en,es' + ) // eslint-disable-next-line no-process-exit process.exit(1) } @@ -17,20 +20,16 @@ const toLocale = process.argv[5] const removeLocales = process.argv.length > 6 ? process.argv[6] : '' function main() { - try { - const spaceExport = SpaceExport.fromJsonFile(fromFile) - const migrator = new LocaleMigrator(fromLocale, toLocale) - const remover = new LocaleRemover(removeLocales.split(','), toLocale) - console.log('Removing locales', remover.removeLocs) + const spaceExport = SpaceExport.fromJsonFile(fromFile) + const migrator = new LocaleMigrator(fromLocale, toLocale) + const remover = new LocaleRemover(removeLocales.split(','), toLocale) + console.log('Removing locales', remover.removeLocs) - migrator.migrate(spaceExport) - remover.remove(spaceExport) + migrator.migrate(spaceExport) + remover.remove(spaceExport) - spaceExport.write(toFile) - console.log('done') - } catch (e) { - console.error(e) - } + spaceExport.write(toFile) + console.log('done') } main() diff --git a/packages/botonic-plugin-contentful/tests/contentful/contents/asset.test.ts b/packages/botonic-plugin-contentful/tests/contentful/contents/asset.test.ts index 9bb737544f..7f2231a04e 100644 --- a/packages/botonic-plugin-contentful/tests/contentful/contents/asset.test.ts +++ b/packages/botonic-plugin-contentful/tests/contentful/contents/asset.test.ts @@ -1,7 +1,7 @@ import { testContentful } from '../contentful.helper' import { expectImgUrlIs } from './image.test' -const TEST_ASSET_ID = '1T0ntgNJnDUSwz59zGMZO6' +export const TEST_ASSET_ID = '1T0ntgNJnDUSwz59zGMZO6' test('TEST: contentful asset', async () => { const sut = testContentful() @@ -10,5 +10,14 @@ test('TEST: contentful asset', async () => { expectImgUrlIs(asset.url, 'red.jpg') expect(asset.type).toEqual('image/jpeg') + expect(asset.id).toEqual(TEST_ASSET_ID) expect(asset.details.size).toEqual(2253) }) + +test('TEST: contentful assets', async () => { + const sut = testContentful() + + const assets = await sut.assets() + expect(assets.length).toBeGreaterThanOrEqual(3) + expect(assets.map(a => a.id)).toContain(TEST_ASSET_ID) +}) diff --git a/packages/botonic-plugin-contentful/tests/contentful/manage/manage-contentful-asset.test.ts b/packages/botonic-plugin-contentful/tests/contentful/manage/manage-contentful-asset.test.ts new file mode 100644 index 0000000000..a0c6d374fe --- /dev/null +++ b/packages/botonic-plugin-contentful/tests/contentful/manage/manage-contentful-asset.test.ts @@ -0,0 +1,24 @@ +import { ENGLISH, SPANISH } from '../../../src/nlp' +import { testManageContentful } from './manage-contentful.helper' +import { ManageContext } from '../../../src/manage-cms' +import { TEST_ASSET_ID } from '../contents/asset.test' + +// Since the tests modify contentful contents, they might fail if executed +// more than once simultaneously (eg from 2 different branches from CI) +describe('ManageContentful assets', () => { + function ctxt(ctx: Partial): ManageContext { + return { ...ctx, preview: false } as ManageContext + } + + test('TEST: copyAssetFile', async () => { + const sut = testManageContentful() + + try { + // ACT + await sut.copyAssetFile(ctxt({ locale: SPANISH }), TEST_ASSET_ID, ENGLISH) + } finally { + // RESTORE + await sut.removeAssetFile(ctxt({ locale: SPANISH }), TEST_ASSET_ID) + } + }) +}) diff --git a/packages/botonic-plugin-contentful/tests/contentful/manage/manage-contentful.test.ts b/packages/botonic-plugin-contentful/tests/contentful/manage/manage-contentful.test.ts index 5a679712eb..e0a145c753 100644 --- a/packages/botonic-plugin-contentful/tests/contentful/manage/manage-contentful.test.ts +++ b/packages/botonic-plugin-contentful/tests/contentful/manage/manage-contentful.test.ts @@ -14,7 +14,7 @@ function ctxt(ctx: Partial): ManageContext { // Since the tests modify contentful contents, they might fail if executed // more than once simultaneously (eg from 2 different branches from CI) -describe('ManageContentful', () => { +describe('ManageContentful fields', () => { const TEST_MANAGE_CMS_ID = '627QkyJrFo3grJryj0vu6L' test('TEST: updateField on an empty field', async () => { @@ -34,7 +34,7 @@ describe('ManageContentful', () => { ) } // ACT - await sut.updateField( + await sut.updateField( ctxt({ locale: SPANISH }), old.contentId, ContentFieldType.TEXT, @@ -48,7 +48,7 @@ describe('ManageContentful', () => { }) } finally { // RESTORE - await sut.updateField( + await sut.updateField( ctxt({ locale: SPANISH, allowOverwrites: true }), old.contentId, ContentFieldType.TEXT, @@ -104,7 +104,7 @@ describe('ManageContentful', () => { try { // ACT - await sut.copyField( + await sut.copyField( ctxt({ locale: TO_LOCALE }), oldContent.contentId, ContentFieldType.BUTTONS, @@ -120,7 +120,7 @@ describe('ManageContentful', () => { }) } finally { // RESTORE - await sut.updateField( + await sut.updateField( ctxt({ locale: TO_LOCALE, allowOverwrites: true }), oldContent.contentId, ContentFieldType.BUTTONS, diff --git a/packages/botonic-plugin-contentful/tests/tools/l10n/csv-import.test.ts b/packages/botonic-plugin-contentful/tests/tools/l10n/csv-import.test.ts index f6a6b0ac51..2b9c6cdfe3 100644 --- a/packages/botonic-plugin-contentful/tests/tools/l10n/csv-import.test.ts +++ b/packages/botonic-plugin-contentful/tests/tools/l10n/csv-import.test.ts @@ -61,6 +61,16 @@ test('TEST: CsvImport read text & carousel', async () => { test('TEST: StringFieldImporter test', async () => { // using manual mock because mockito was not recognizing the call. maybe because method returns Promise to void? class MockCms implements ManageCms { + copyAssetFile( + context: ManageContext, + assetId: string, + fromLocale: string + ): Promise { + fail("shouldn't be called") + } + removeAssetFile(context: ManageContext, assetId: string): Promise { + fail("shouldn't be called") + } numCalls = 0 getDefaultLocale(): Promise {