Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented nested global fields #1717

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default class GlobalFieldsExport extends BaseClass {
if (skip) {
this.qs.skip = skip;
}
let globalFieldsFetchResponse = await this.stackAPIClient.globalField().query(this.qs).find();
let globalFieldsFetchResponse = await this.stackAPIClient.globalField({api_version: '3.2'}).query(this.qs).find();
if (Array.isArray(globalFieldsFetchResponse.items) && globalFieldsFetchResponse.items.length > 0) {
this.sanitizeAttribs(globalFieldsFetchResponse.items);
skip += this.globalFieldsConfig.limit || 100;
Expand Down
2 changes: 2 additions & 0 deletions packages/contentstack-import/src/import/modules/base-class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ export default abstract class BaseClass {
return this.stack.contentType().create(apiData).then(onSuccess).catch(onReject);
case 'update-cts':
return apiData.update().then(onSuccess).catch(onReject);
case 'create-gfs':
return this.stack.globalField({api_version: '3.2'}).create(apiData).then(onSuccess).catch(onReject);
case 'update-gfs':
return apiData.update().then(onSuccess).catch(onReject);
case 'create-environments':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export default class ContentTypesImport extends BaseClass {
log(this.importConfig, `Updated the global field ${uid} with content type references`, 'info');
};
const onReject = ({ error, apiData: { uid } = undefined }: any) => {
log(this.importConfig, `failed to update the global field '${uid}' ${formatError(error)}`, 'error');
log(this.importConfig, `Failed to update the global field '${uid}' ${formatError(error)}`, 'error');
};
return await this.makeConcurrentCall({
processName: 'Update pending global fields',
Expand Down Expand Up @@ -266,8 +266,9 @@ export default class ContentTypesImport extends BaseClass {
this.importConfig.preserveStackVersion,
this.installedExtensions,
);
apiOptions.apiData = globalField;
const globalFieldPayload = this.stack.globalField(uid);
const globalFieldPayload = this.stack.globalField(
uid, { api_version: '3.2' },
);
Object.assign(globalFieldPayload, cloneDeep(globalField));
apiOptions.apiData = globalFieldPayload;
return apiOptions;
Expand Down
95 changes: 67 additions & 28 deletions packages/contentstack-import/src/import/modules/global-fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
*/

import * as path from 'path';
import { isEmpty, cloneDeep } from 'lodash';
import { isEmpty, cloneDeep, map, find } from 'lodash';
import { cliux, sanitizePath } from '@contentstack/cli-utilities';
import { GlobalFieldData } from '@contentstack/management/types/stack/globalField';
import { GlobalFieldData, GlobalField } from '@contentstack/management/types/stack/globalField';
import { fsUtil, log, formatError, fileHelper, lookupExtension, removeReferenceFields } from '../../utils';
import { ImportConfig, ModuleClassParams } from '../../types';
import BaseClass, { ApiOptions } from './base-class';
import { gfSchemaTemplate } from '../../utils/global-field-helper';


export default class ImportGlobalFields extends BaseClass {
private gFsMapperPath: string;
Expand Down Expand Up @@ -73,8 +75,12 @@ export default class ImportGlobalFields extends BaseClass {
((await fsUtil.readFile(this.marketplaceAppMapperPath)) as any) || { extension_uid: {} }
).extension_uid;

await this.importGFs();
fsUtil.writeFile(this.gFsPendingPath, this.pendingGFs);
await this.seedGFs();
if (this.seedGFs?.length) fsUtil.writeFile(this.gFsPendingPath, this.pendingGFs);
log(this.importConfig, 'Created Global Fields', 'success');

await this.updateGFs();
log(this.importConfig, 'Updated Global Fields', 'success');

if (this.importConfig.replaceExisting && this.existingGFs.length > 0) {
await this.replaceGFs().catch((error: Error) => {
Expand All @@ -85,46 +91,79 @@ export default class ImportGlobalFields extends BaseClass {
log(this.config, 'Global fields import has been completed!', 'info');
}

async importGFs() {
async seedGFs(): Promise<any> {
const onSuccess = ({ response: globalField, apiData: { uid } = undefined }: any) => {
this.createdGFs.push(globalField);
this.gFsUidMapper[uid] = globalField;
fsUtil.writeFile(this.gFsUidMapperPath, this.gFsUidMapper);
log(this.config, 'Global field ' + uid + ' created successfully', 'success');
log(this.importConfig, `Global field ${globalField.uid} created successfully`, 'success');
};
const onReject = ({ error, apiData: globalField = undefined }: any) => {
const uid = globalField.uid;
const uid = globalField?.uid;
if (error?.errors?.title) {
if (this.importConfig.replaceExisting) {
this.existingGFs.push(globalField);
}
if (!this.importConfig.skipExisting) {
log(this.importConfig, `Global fields '${uid}' already exist`, 'info');
log(this.importConfig, `Global fields '${globalField?.global_field?.uid}' already exist`, 'info');
}
} else {
log(this.importConfig, `Global fields '${uid}' failed to import`, 'error');
log(this.importConfig, formatError(error), 'error');
this.failedGFs.push({ uid });
}
};
return await this.makeConcurrentCall({
processName: 'Import global fields',
apiContent: this.gFs,
apiParams: {
serializeData: this.serializeGFs.bind(this),
reject: onReject.bind(this),
resolve: onSuccess.bind(this),
entity: 'create-gfs',
includeParamOnCompletion: true,
},
concurrencyLimit: this.reqConcurrency,
});
}

return await this.makeConcurrentCall(
{
processName: 'Import global fields',
apiContent: this.gFs,
apiParams: {
reject: onReject.bind(this),
resolve: onSuccess.bind(this),
entity: 'create-gfs',
includeParamOnCompletion: true,
},
concurrencyLimit: this.reqConcurrency,
/**
* @method serializeGFs
* @param {ApiOptions} apiOptions ApiOptions
* @returns {ApiOptions} ApiOptions
*/
serializeGFs(apiOptions: ApiOptions): ApiOptions {
const { apiData: globalField } = apiOptions;
const updatedGF = cloneDeep(gfSchemaTemplate);
updatedGF.global_field.uid = globalField.uid;
updatedGF.global_field.title = globalField.title;
apiOptions.apiData = updatedGF;
return apiOptions;
}

async updateGFs(): Promise<any> {
const onSuccess = ({ response: globalField, apiData: { uid } = undefined }: any) => {
log(this.importConfig, `Updated the global field ${uid}`, 'info');
};
const onReject = ({ error, apiData: { uid } = undefined }: any) => {
log(this.importConfig, `Failed to update the global field '${uid}' ${formatError(error)}`, 'error');
};

return await this.makeConcurrentCall({
processName: 'Update Global Fields',
apiContent: this.gFs,
apiParams: {
reject: onReject.bind(this),
resolve: onSuccess.bind(this),
entity: 'update-gfs',
includeParamOnCompletion: true,
},
this.createGFs.bind(this),
concurrencyLimit: this.reqConcurrency,
},
this.updateSerializedGFs.bind(this),
);
}

async createGFs({
async updateSerializedGFs({
apiParams,
element: globalField,
isLastRequest,
Expand All @@ -141,16 +180,16 @@ export default class ImportGlobalFields extends BaseClass {
this.pendingGFs.push(globalField.uid);
}
return this.stack
.globalField()
.create({ global_field: globalField as GlobalFieldData })
.then((response) => {
.globalField(globalField.uid, { api_version: '3.2' })
.update({ global_field: globalField })
.then((response: GlobalField) => {
apiParams.resolve({
response,
apiData: globalField,
});
resolve(true);
})
.catch((error) => {
.catch((error: unknown) => {
apiParams.reject({
error,
apiData: globalField,
Expand Down Expand Up @@ -193,13 +232,13 @@ export default class ImportGlobalFields extends BaseClass {
}

/**
* @method serializeUpdateGFs
* @method serializeReplaceGFs
* @param {ApiOptions} apiOptions ApiOptions
* @returns {ApiOptions} ApiOptions
*/
serializeReplaceGFs(apiOptions: ApiOptions): ApiOptions {
const { apiData: globalField } = apiOptions;
const globalFieldPayload = this.stack.globalField(globalField.uid);
const globalFieldPayload = this.stack.globalField(globalField.uid, { api_version: '3.2' });
Object.assign(globalFieldPayload, cloneDeep(globalField), {
stackHeaders: globalFieldPayload.stackHeaders,
});
Expand Down
27 changes: 27 additions & 0 deletions packages/contentstack-import/src/utils/global-field-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Global field utiles
* schema template
* remove reference fields
* suppress mandatory fields
*/

export const gfSchemaTemplate = {
"global_field": {
"title": "Seed",
"uid": "",
"schema": [
{
"display_name": "Title",
"uid": "title",
"data_type": "text",
"field_metadata": {
"_default": true
},
"unique": false,
"mandatory": true,
"multiple": false
}
],
"description": "Seed "
}
};
Loading