Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env-example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ENVIRONMENT=NON_PROD
4 changes: 2 additions & 2 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
fileignoreconfig:
- filename: package-lock.json
checksum: 4e00357992422982d516ee3231f87a1f98f27c12788c63a6a089cafa059b6b9e
- filename: .env-example
checksum: 591f1e672d4df287107092b8fd37c27913e09225c6ced55293e1d459b1119d05
version: '1.0'
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@contentstack/cli-cm-export-query",
"description": "Contentstack CLI plugin to export content from stack",
"version": "1.0.0-beta.1",
"version": "1.0.0-beta.2",
"author": "Contentstack",
"bugs": "https://github.com/contentstack/cli/issues",
"dependencies": {
Expand Down
Empty file added snyk_output.log
Empty file.
18 changes: 14 additions & 4 deletions src/commands/cm/stacks/export-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '@contentstack/cli-utilities';
import { QueryExporter } from '../../../core/query-executor';
import { QueryExportConfig } from '../../../types';
import { log, setupQueryExportConfig } from '../../../utils';
import { log, setupQueryExportConfig, setupBranches } from '../../../utils';

export default class ExportQueryCommand extends Command {
static description = 'Export content from a stack using query-based filtering';
Expand Down Expand Up @@ -63,8 +63,6 @@ export default class ExportQueryCommand extends Command {
}),
};

static aliases = ['cm:export-query'];

async run(): Promise<void> {
try {
const { flags } = await this.parse(ExportQueryCommand);
Expand All @@ -79,8 +77,20 @@ export default class ExportQueryCommand extends Command {
}

this.exportDir = sanitizePath(exportQueryConfig.exportDir);
// Initialize and run query export

// Initialize management API client
const managementAPIClient: ContentstackClient = await managementSDKClient(exportQueryConfig);

// Setup and validate branch configuration
const stackAPIClient = managementAPIClient.stack({
api_key: exportQueryConfig.stackApiKey,
management_token: exportQueryConfig.managementToken,
});

// Setup branches (validate branch or set default to 'main')
await setupBranches(exportQueryConfig, stackAPIClient);

// Initialize and run query export
const queryExporter = new QueryExporter(managementAPIClient, exportQueryConfig);
await queryExporter.execute();

Expand Down
2 changes: 0 additions & 2 deletions src/core/module-exporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ export class ModuleExporter {
// Build command arguments
const cmd = this.buildExportCommand(moduleName, options);

log(this.exportQueryConfig, `Running export command: ${cmd.join(' ')}`, 'debug');

// Configurable delay
const delay = this.exportQueryConfig.exportDelayMs || 2000;
await new Promise((resolve) => setTimeout(resolve, delay));
Expand Down
66 changes: 66 additions & 0 deletions src/utils/branch-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as path from 'path';
import { sanitizePath } from '@contentstack/cli-utilities';
import { QueryExportConfig } from '../types';
import { fsUtil } from './file-helper';
import { log } from './logger';

/**
* Validates and sets up branch configuration for the stack
*
* @param config The export configuration
* @param stackAPIClient The stack API client
* @returns Promise that resolves when branch setup is complete
*/
export const setupBranches = async (config: QueryExportConfig, stackAPIClient: any): Promise<void> => {
if (typeof config !== 'object') {
throw new Error('Invalid config to setup the branch');
}

try {
if (config.branchName) {
// Check if the specified branch exists
log(config, `Validating branch: ${config.branchName}`, 'info');

const result = await stackAPIClient
.branch(config.branchName)
.fetch()
.catch((err: Error): any => {
log(config, `Error fetching branch: ${err.message}`, 'error');
return null;
});

if (result && typeof result === 'object') {
log(config, `Branch '${config.branchName}' found`, 'success');
} else {
throw new Error(`No branch found with the name '${config.branchName}'`);
}
} else {
// If no branch name provided, check if the stack has branches
log(config, 'No branch specified, checking if stack has branches', 'info');

const result = await stackAPIClient
.branch()
.query()
.find()
.catch((): any => {
log(config, 'Stack does not have branches', 'info');
return null;
});

if (result && result.items && Array.isArray(result.items) && result.items.length > 0) {
// Set default branch to 'main' if it exists
config.branchName = 'main';
} else {
// Stack doesn't have branches
log(config, 'Stack does not have branches', 'info');
return;
}
}
config.branchEnabled = true;
} catch (error) {
log(config, `Error setting up branches: ${error.message}`, 'error');
throw error;
}
};

export default setupBranches;
12 changes: 6 additions & 6 deletions src/utils/config-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@ export async function setupQueryExportConfig(flags: any): Promise<QueryExportCon
// Handle authentication
if (flags.alias) {
const { token, apiKey } = configHandler.get(`tokens.${flags.alias}`) || {};
config.managementToken = token;
config.stackApiKey = apiKey;
if (!config.managementToken) {
exportQueryConfig.managementToken = token;
exportQueryConfig.stackApiKey = apiKey;
if (!exportQueryConfig.managementToken) {
throw new Error(`No management token found on given alias ${flags.alias}`);
}
}

if (!config.managementToken) {
if (!exportQueryConfig.managementToken) {
if (!isAuthenticated()) {
throw new Error('Please login or provide an alias for the management token');
} else {
config.stackApiKey = flags['stack-api-key'] || (await askAPIKey());
if (typeof config.stackApiKey !== 'string') {
exportQueryConfig.stackApiKey = flags['stack-api-key'] || (await askAPIKey());
if (typeof exportQueryConfig.stackApiKey !== 'string') {
throw new Error('Invalid API key received');
}
}
Expand Down
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './config-handler';
export * from './content-type-helper';
export * from './dependency-resolver';
export * from './referenced-asset-handler';
export { setupBranches } from './branch-helper';
18 changes: 13 additions & 5 deletions src/utils/referenced-asset-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,21 @@ export class AssetReferenceHandler {
}
}

let assetUrlRegex = '';
let assetUIDMatchIndex;
if (process.env.ENVIRONMENT === 'NON_PROD') {
assetUrlRegex = '(https://.*?/v3/assets/(.*?)/(.*?)/(.*?)/(.*?)(?="))';
assetUIDMatchIndex = 3;
} else {
assetUrlRegex =
'(https://(assets|(eu-|azure-na-|azure-eu-|gcp-na-|gcp-eu-)?images).contentstack.(io|com)/v3/assets/(.*?)/(.*?)/(.*?)/(.*?)(?="))';
assetUIDMatchIndex = 6;
}

// Pattern 2: Contentstack asset URLs
const urlRegex = new RegExp(
'(https://(assets|(eu-|azure-na-|azure-eu-|gcp-na-|gcp-eu-)?images).contentstack.(io|com)/v3/assets/(.*?)/(.*?)/(.*?)/(.*?)(?="))',
'g',
);
const urlRegex = new RegExp(assetUrlRegex, 'g');
while ((match = urlRegex.exec(content)) !== null) {
const assetUID = match[6]; // The asset UID is in the 6th capture group
const assetUID = match[assetUIDMatchIndex]; // The asset UID is in the 6th capture group
if (assetUID) {
assetUIDs.add(assetUID);
}
Expand Down