Skip to content

Commit cbd6d7f

Browse files
author
naman-contentstack
committed
chore: made implementation consistent with other modules
1 parent bfb990b commit cbd6d7f

20 files changed

Lines changed: 279 additions & 81 deletions

File tree

.talismanrc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
fileignoreconfig:
2-
- filename: pnpm-lock.yaml
3-
checksum: 9ad01ca900e007d8ea4f5ffcef9f166665aba852b3b9a0a5f4506c04fd362138
2+
- filename: packages/contentstack-asset-management/src/import/fields.ts
3+
checksum: e46c3eb94bba78ae06af0139a1b0fd4113c3dcfe879ed40cfe77659781526d5c
4+
- filename: packages/contentstack-asset-management/src/import/asset-types.ts
5+
checksum: 3525703fd2ac0f7ab3e963966c0abfa53d87c09f73dabab0889c7f41a5f1b003
6+
- filename: packages/contentstack-import/src/types/default-config.ts
7+
checksum: 1c09acba953cfd7058a3e0d63f0a9bfbb8f28e903538eaa015fdc611402bbd4f
8+
- filename: packages/contentstack-asset-management/src/types/asset-management-api.ts
9+
checksum: 9394e67f4d0dea9ad27a8592adf99441f40731b788335cd7699c78205bdaad58
10+
- filename: packages/contentstack-import/src/import/modules/assets.ts
11+
checksum: 98cea49283eb5d975168dc51e27b0e2fb541a9ed9b4368206f99b434e104f096
412
version: '1.0'

packages/contentstack-asset-management/src/constants/index.ts

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,36 @@
1-
export const BATCH_SIZE = 50;
2-
export const CHUNK_FILE_SIZE_MB = 1;
1+
/** Fallback when export/import do not pass `chunkWriteBatchSize`. */
2+
export const FALLBACK_AM_CHUNK_WRITE_BATCH_SIZE = 50;
3+
/** Fallback when export/import do not pass `chunkFileSizeMb`. */
4+
export const FALLBACK_AM_CHUNK_FILE_SIZE_MB = 1;
5+
/** Fallback when import does not pass `apiConcurrency`. */
6+
export const FALLBACK_AM_API_CONCURRENCY = 5;
7+
/** @deprecated Use FALLBACK_AM_API_CONCURRENCY */
8+
export const DEFAULT_AM_API_CONCURRENCY = FALLBACK_AM_API_CONCURRENCY;
39

4-
/** Default parallel AM API calls when import caller does not set apiConcurrency. */
5-
export const DEFAULT_AM_API_CONCURRENCY = 5;
10+
/** Fallback strip lists when import options omit `fieldsImportInvalidKeys` / `assetTypesImportInvalidKeys`. */
11+
export const FALLBACK_FIELDS_IMPORT_INVALID_KEYS = [
12+
'created_at',
13+
'created_by',
14+
'updated_at',
15+
'updated_by',
16+
'is_system',
17+
'asset_types_count',
18+
] as const;
19+
export const FALLBACK_ASSET_TYPES_IMPORT_INVALID_KEYS = [
20+
'created_at',
21+
'created_by',
22+
'updated_at',
23+
'updated_by',
24+
'is_system',
25+
'category',
26+
'preview_image_url',
27+
'category_detail',
28+
] as const;
629

7-
/**
8-
* Mapper output paths — must stay aligned with contentstack-import `PATH_CONSTANTS`
9-
* (`mapper` / `assets` / uid, url, space-uid file names).
10-
*/
11-
export const IMPORT_ASSETS_MAPPER_DIR_SEGMENTS = ['mapper', 'assets'] as const;
12-
export const IMPORT_ASSETS_MAPPER_FILES = {
13-
UID_MAPPING: 'uid-mapping.json',
14-
URL_MAPPING: 'url-mapping.json',
15-
SPACE_UID_MAPPING: 'space-uid-mapping.json',
16-
} as const;
30+
/** @deprecated Use FALLBACK_AM_CHUNK_WRITE_BATCH_SIZE */
31+
export const BATCH_SIZE = FALLBACK_AM_CHUNK_WRITE_BATCH_SIZE;
32+
/** @deprecated Use FALLBACK_AM_CHUNK_FILE_SIZE_MB */
33+
export const CHUNK_FILE_SIZE_MB = FALLBACK_AM_CHUNK_FILE_SIZE_MB;
1734

1835
/**
1936
* Main process name for Asset Management 2.0 export (single progress bar).

packages/contentstack-asset-management/src/export/base.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ import { FsUtility, log, CLIProgressManager, configHandler } from '@contentstack
55
import type { AssetManagementAPIConfig } from '../types/asset-management-api';
66
import type { ExportContext } from '../types/export-types';
77
import { AssetManagementAdapter } from '../utils/asset-management-api-adapter';
8-
import { AM_MAIN_PROCESS_NAME } from '../constants/index';
9-
import { BATCH_SIZE, CHUNK_FILE_SIZE_MB } from '../constants/index';
8+
import {
9+
AM_MAIN_PROCESS_NAME,
10+
FALLBACK_AM_CHUNK_FILE_SIZE_MB,
11+
FALLBACK_AM_CHUNK_WRITE_BATCH_SIZE,
12+
} from '../constants/index';
1013

1114
export type { ExportContext };
1215

@@ -83,17 +86,19 @@ export class AssetManagementExportAdapter extends AssetManagementAdapter {
8386
await writeFile(pResolve(dir, indexFileName), '{}');
8487
return;
8588
}
89+
const chunkMb = this.exportContext.chunkFileSizeMb ?? FALLBACK_AM_CHUNK_FILE_SIZE_MB;
90+
const batchSize = this.exportContext.chunkWriteBatchSize ?? FALLBACK_AM_CHUNK_WRITE_BATCH_SIZE;
8691
const fs = new FsUtility({
8792
basePath: dir,
8893
indexFileName,
89-
chunkFileSize: CHUNK_FILE_SIZE_MB,
94+
chunkFileSize: chunkMb,
9095
moduleName,
9196
fileExt: 'json',
9297
metaPickKeys,
9398
keepMetadata: true,
9499
});
95-
for (let i = 0; i < items.length; i += BATCH_SIZE) {
96-
const batch = items.slice(i, i + BATCH_SIZE);
100+
for (let i = 0; i < items.length; i += batchSize) {
101+
const batch = items.slice(i, i + batchSize);
97102
fs.writeIntoFile(batch as Record<string, string>[], { mapKeyVal: true });
98103
}
99104
fs.completeFile(true);

packages/contentstack-asset-management/src/export/spaces.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,18 @@ export class ExportSpaces {
2929
}
3030

3131
async start(): Promise<void> {
32-
const { linkedWorkspaces, exportDir, branchName, assetManagementUrl, org_uid, apiKey, context, securedAssets } =
33-
this.options;
32+
const {
33+
linkedWorkspaces,
34+
exportDir,
35+
branchName,
36+
assetManagementUrl,
37+
org_uid,
38+
apiKey,
39+
context,
40+
securedAssets,
41+
chunkWriteBatchSize,
42+
chunkFileSizeMb,
43+
} = this.options;
3444

3545
if (!linkedWorkspaces.length) {
3646
log.debug('No linked workspaces to export', context);
@@ -60,6 +70,8 @@ export class ExportSpaces {
6070
spacesRootPath,
6171
context,
6272
securedAssets,
73+
chunkWriteBatchSize,
74+
chunkFileSizeMb,
6375
};
6476

6577
const sharedFieldsDir = pResolve(spacesRootPath, 'fields');

packages/contentstack-asset-management/src/import/asset-types.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ import { log } from '@contentstack/cli-utilities';
44

55
import type { AssetManagementAPIConfig, ImportContext } from '../types/asset-management-api';
66
import { AssetManagementImportAdapter } from './base';
7-
import { PROCESS_NAMES, PROCESS_STATUS } from '../constants/index';
7+
import { FALLBACK_ASSET_TYPES_IMPORT_INVALID_KEYS, PROCESS_NAMES, PROCESS_STATUS } from '../constants/index';
88
import { runInBatches } from '../utils/concurrent-batch';
9-
10-
const STRIP_KEYS = ['created_at', 'created_by', 'updated_at', 'updated_by', 'is_system', 'category', 'preview_image_url', 'category_detail'];
9+
import { readChunkedJsonItems } from '../utils/chunked-json-read';
1110

1211
/**
1312
* Reads shared asset types from `spaces/asset_types/asset-types.json` and POSTs
@@ -28,8 +27,10 @@ export default class ImportAssetTypes extends AssetManagementImportAdapter {
2827
async start(): Promise<void> {
2928
await this.init();
3029

30+
const stripKeys = this.importContext.assetTypesImportInvalidKeys ?? [...FALLBACK_ASSET_TYPES_IMPORT_INVALID_KEYS];
3131
const dir = this.getAssetTypesDir();
32-
const items = await this.readAllChunkedJson<Record<string, unknown>>(dir, 'asset-types.json');
32+
const indexName = this.importContext.assetTypesFileName ?? 'asset-types.json';
33+
const items = await readChunkedJsonItems<Record<string, unknown>>(dir, indexName, this.importContext.context);
3334

3435
if (items.length === 0) {
3536
log.debug('No shared asset types to import', this.importContext.context);
@@ -64,8 +65,8 @@ export default class ImportAssetTypes extends AssetManagementImportAdapter {
6465

6566
const existing = existingByUid.get(uid);
6667
if (existing) {
67-
const exportedClean = omit(assetType, STRIP_KEYS);
68-
const existingClean = omit(existing, STRIP_KEYS);
68+
const exportedClean = omit(assetType, stripKeys);
69+
const existingClean = omit(existing, stripKeys);
6970
if (!isEqual(exportedClean, existingClean)) {
7071
log.warn(
7172
`Asset type "${uid}" already exists in the target org with a different definition. Skipping — to apply the exported definition, delete the asset type from the target org first.`,
@@ -78,7 +79,7 @@ export default class ImportAssetTypes extends AssetManagementImportAdapter {
7879
continue;
7980
}
8081

81-
toCreate.push({ uid, payload: omit(assetType, STRIP_KEYS) as Record<string, unknown> });
82+
toCreate.push({ uid, payload: omit(assetType, stripKeys) as Record<string, unknown> });
8283
}
8384

8485
await runInBatches(toCreate, this.apiConcurrency, async ({ uid, payload }) => {

packages/contentstack-asset-management/src/import/assets.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { log } from '@contentstack/cli-utilities';
55
import type { AssetManagementAPIConfig, ImportContext } from '../types/asset-management-api';
66
import { AssetManagementImportAdapter } from './base';
77
import { getArrayFromResponse } from '../utils/export-helpers';
8+
import { readChunkedJsonItems } from '../utils/chunked-json-read';
89
import { runInBatches } from '../utils/concurrent-batch';
910
import { PROCESS_NAMES, PROCESS_STATUS } from '../constants/index';
1011

@@ -42,10 +43,11 @@ export default class ImportAssets extends AssetManagementImportAdapter {
4243
*/
4344
private async loadExportedAssetItems(spaceDir: string): Promise<AssetRecord[] | null> {
4445
const assetsDir = pResolve(spaceDir, 'assets');
45-
if (!existsSync(join(assetsDir, 'assets.json'))) {
46+
const assetsIndex = this.importContext.assetsFileName ?? 'assets.json';
47+
if (!existsSync(join(assetsDir, assetsIndex))) {
4648
return null;
4749
}
48-
return this.readAllChunkedJson<AssetRecord>(assetsDir, 'assets.json');
50+
return readChunkedJsonItems<AssetRecord>(assetsDir, assetsIndex, this.importContext.context);
4951
}
5052

5153
/**
@@ -95,14 +97,15 @@ export default class ImportAssets extends AssetManagementImportAdapter {
9597
// 1. Import folders
9698
// -----------------------------------------------------------------------
9799
const folderUidMap: Record<string, string> = {};
98-
const foldersFilePath = join(assetsDir, 'folders.json');
100+
const foldersFileName = this.importContext.foldersFileName ?? 'folders.json';
101+
const foldersFilePath = join(assetsDir, foldersFileName);
99102

100103
if (existsSync(foldersFilePath)) {
101104
let foldersData: unknown;
102105
try {
103106
foldersData = JSON.parse(readFileSync(foldersFilePath, 'utf8'));
104107
} catch (e) {
105-
log.debug(`Could not read folders.json: ${e}`, this.importContext.context);
108+
log.debug(`Could not read ${foldersFileName}: ${e}`, this.importContext.context);
106109
}
107110

108111
if (foldersData) {

packages/contentstack-asset-management/src/import/base.ts

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { resolve as pResolve } from 'node:path';
2-
import { FsUtility, log, CLIProgressManager, configHandler } from '@contentstack/cli-utilities';
2+
import { CLIProgressManager, configHandler } from '@contentstack/cli-utilities';
33

44
import type { AssetManagementAPIConfig, ImportContext } from '../types/asset-management-api';
55
import { AssetManagementAdapter } from '../utils/asset-management-api-adapter';
6-
import { AM_MAIN_PROCESS_NAME, DEFAULT_AM_API_CONCURRENCY } from '../constants/index';
6+
import { AM_MAIN_PROCESS_NAME, FALLBACK_AM_API_CONCURRENCY } from '../constants/index';
7+
import { readChunkedJsonItems } from '../utils/chunked-json-read';
78

89
export type { ImportContext };
910

@@ -63,39 +64,21 @@ export class AssetManagementImportAdapter extends AssetManagementAdapter {
6364

6465
/** Parallel AM API limit for import batches. */
6566
protected get apiConcurrency(): number {
66-
return this.importContext.apiConcurrency ?? DEFAULT_AM_API_CONCURRENCY;
67+
return this.importContext.apiConcurrency ?? FALLBACK_AM_API_CONCURRENCY;
6768
}
6869

6970
protected getAssetTypesDir(): string {
70-
return pResolve(this.importContext.spacesRootPath, 'asset_types');
71+
return pResolve(this.importContext.spacesRootPath, this.importContext.assetTypesDir ?? 'asset_types');
7172
}
7273

7374
protected getFieldsDir(): string {
74-
return pResolve(this.importContext.spacesRootPath, 'fields');
75+
return pResolve(this.importContext.spacesRootPath, this.importContext.fieldsDir ?? 'fields');
7576
}
7677

7778
/**
78-
* Reads all items from a FsUtility chunked JSON store (index file + chunk files).
79-
* Returns a flat array of all items across all chunks.
79+
* Reads all items from a chunked JSON store via {@link readChunkedJsonItems} (FsUtility).
8080
*/
8181
protected async readAllChunkedJson<T = Record<string, unknown>>(dir: string, indexFileName: string): Promise<T[]> {
82-
try {
83-
const fs = new FsUtility({ basePath: dir, indexFileName });
84-
const indexer = fs.indexFileContent;
85-
const items: T[] = [];
86-
for (const _ in indexer) {
87-
const chunk = await fs.readChunkFiles.next().catch((err: unknown): null => {
88-
log.debug(`Error reading chunk: ${err}`, this.importContext.context);
89-
return null;
90-
});
91-
if (chunk) {
92-
items.push(...(Object.values(chunk as Record<string, T>)));
93-
}
94-
}
95-
return items;
96-
} catch (err) {
97-
log.debug(`readAllChunkedJson failed for ${dir}/${indexFileName}: ${err}`, this.importContext.context);
98-
return [];
99-
}
82+
return readChunkedJsonItems<T>(dir, indexFileName, this.importContext.context);
10083
}
10184
}

packages/contentstack-asset-management/src/import/fields.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ import { log } from '@contentstack/cli-utilities';
44

55
import type { AssetManagementAPIConfig, ImportContext } from '../types/asset-management-api';
66
import { AssetManagementImportAdapter } from './base';
7-
import { PROCESS_NAMES, PROCESS_STATUS } from '../constants/index';
7+
import { FALLBACK_FIELDS_IMPORT_INVALID_KEYS, PROCESS_NAMES, PROCESS_STATUS } from '../constants/index';
88
import { runInBatches } from '../utils/concurrent-batch';
9-
10-
const STRIP_KEYS = ['created_at', 'created_by', 'updated_at', 'updated_by', 'is_system', 'asset_types_count'];
9+
import { readChunkedJsonItems } from '../utils/chunked-json-read';
1110

1211
/**
1312
* Reads shared fields from `spaces/fields/fields.json` and POSTs each to the
@@ -28,8 +27,10 @@ export default class ImportFields extends AssetManagementImportAdapter {
2827
async start(): Promise<void> {
2928
await this.init();
3029

30+
const stripKeys = this.importContext.fieldsImportInvalidKeys ?? [...FALLBACK_FIELDS_IMPORT_INVALID_KEYS];
3131
const dir = this.getFieldsDir();
32-
const items = await this.readAllChunkedJson<Record<string, unknown>>(dir, 'fields.json');
32+
const indexName = this.importContext.fieldsFileName ?? 'fields.json';
33+
const items = await readChunkedJsonItems<Record<string, unknown>>(dir, indexName, this.importContext.context);
3334

3435
if (items.length === 0) {
3536
log.debug('No shared fields to import', this.importContext.context);
@@ -64,8 +65,8 @@ export default class ImportFields extends AssetManagementImportAdapter {
6465

6566
const existing = existingByUid.get(uid);
6667
if (existing) {
67-
const exportedClean = omit(field, STRIP_KEYS);
68-
const existingClean = omit(existing, STRIP_KEYS);
68+
const exportedClean = omit(field, stripKeys);
69+
const existingClean = omit(existing, stripKeys);
6970
if (!isEqual(exportedClean, existingClean)) {
7071
log.warn(
7172
`Field "${uid}" already exists in the target org with a different definition. Skipping — to apply the exported definition, delete the field from the target org first.`,
@@ -78,7 +79,7 @@ export default class ImportFields extends AssetManagementImportAdapter {
7879
continue;
7980
}
8081

81-
toCreate.push({ uid, payload: omit(field, STRIP_KEYS) as Record<string, unknown> });
82+
toCreate.push({ uid, payload: omit(field, stripKeys) as Record<string, unknown> });
8283
}
8384

8485
await runInBatches(toCreate, this.apiConcurrency, async ({ uid, payload }) => {

0 commit comments

Comments
 (0)