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
8 changes: 4 additions & 4 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -345,13 +345,13 @@ fileignoreconfig:
- filename: src/environments/environment.errors.test.ts
checksum: 33ae71db0a46173c903659438a77d1d441797a8296e0591dbaa48512f32a4cab
- filename: src/stacks/stacks.api.test.ts
checksum: db0c613f10361fa6aafa71cc09d217a3c5a7f924523901469b174807b7b9b237
checksum: 67e00bd82b3c3a96c05585f36a3c3ace7f06eff535bc347fe9937033775cf5cc
- filename: src/stacks/stacks.api.ts
checksum: 7241b9c2c6e946c11849bb5fb21842c03eca09f9ed6c901be198fd544ce8f769
- filename: src/variables/variable.chooser.ts
checksum: 15a6b8a1a974c8d0396ac18d8c9d39c8bc9f70efbf41ce0eef79180ce16b2f04
- filename: src/variables/variable.create.test.ts
checksum: 48b9864639a01e05b8c6de9ce5ef1abad7ba990ecfbe7f47d0df3d3e759344b8
checksum: 71dbed4ca09e86e761026955c7865f12e4f168e3afcf4c4ef664d3de319a2697
- filename: src/variables/variable.dotenv.test.ts
checksum: c3d5e33189736bb42b7c4fac7375cf0307681b34feca4830b49331f55a130f78
- filename: src/variables/variable.dotenv.ts
Expand All @@ -377,13 +377,13 @@ fileignoreconfig:
- filename: src/variables/variable.presenter.ts
checksum: ee8e4dcf75ad7e849d1e8e64ce6a6549d13fc62dc9aa322d96eb2e725d629db5
- filename: src/variables/variable.set.test.ts
checksum: f6a59e15775ea4247ec3860d4d3fad495b1624ed8a9d834c79a3fcc3033df58e
checksum: 68bd428a7f0a26e495cd9f79779cbc9fb5017cdee684594e90a983ea14303a62
- filename: src/variables/variable.sources.test.ts
checksum: 63ad95e998cbc6d33fabe4ce76797e8788b17abec3833b4fed2360eab01a6f79
- filename: src/variables/variable.sources.ts
checksum: c2c7e442c6fed29b11843850ad86c8603540fcc174e1fac79993c8e26d95e8dc
- filename: src/variables/variable.stack.test.ts
checksum: 75db498059c80145ed72ef4ec4c3e787cca31ab1e7e41ad563ec90fa6dfc035d
checksum: d221484c0709aeb6de9359f533ee79d73513a878cace0a6247439917d658ce4f
- filename: src/variables/variable.stack.ts
checksum: 0c5116d39bacccdb6e813357dd091e90274383761832f19d3a9b5433a21d99ae
- filename: src/variables/variable.summary.test.ts
Expand Down
23 changes: 14 additions & 9 deletions src/stacks/stacks.api.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { randomUUID } from 'node:crypto';
import { EXIT_RUNTIME } from '../core/constants';
import type { CmaCollection, CmaSession } from '../transport/cma-client';
import { StackLookupError } from './stack.errors';
import { StacksApi } from './stacks.api';

const TOKEN_A = `cs-${randomUUID()}`;
const TOKEN_B = `cs-${randomUUID()}`;
const TOKEN_C = `cs-${randomUUID()}`;

function session(stacks: CmaCollection | Error, tokens: CmaCollection | Error) {
const asked: { stacks: string[]; tokens: string[] } = { stacks: [], tokens: [] };
const cma = {
Expand Down Expand Up @@ -63,15 +68,15 @@ describe('stacks api', () => {
{},
{
items: [
{ name: 'prod-token', token: 'cs-aaa', scope: [{ module: 'environment', environments: [{ name: 'production' }, { name: 'staging' }] }] },
{ name: 'dev-token', token: 'cs-bbb', scope: [{ module: 'environment', environments: [{ name: 'development' }] }] },
{ name: 'prod-token', token: TOKEN_A, scope: [{ module: 'environment', environments: [{ name: 'production' }, { name: 'staging' }] }] },
{ name: 'dev-token', token: TOKEN_B, scope: [{ module: 'environment', environments: [{ name: 'development' }] }] },
],
},
);

await expect(api.deliveryTokens('blt1')).resolves.toEqual([
{ name: 'prod-token', token: 'cs-aaa', environment: 'production' },
{ name: 'dev-token', token: 'cs-bbb', environment: 'development' },
{ name: 'prod-token', token: TOKEN_A, environment: 'production' },
{ name: 'dev-token', token: TOKEN_B, environment: 'development' },
]);
expect(asked.tokens).toEqual(['blt1']);
});
Expand All @@ -85,20 +90,20 @@ describe('stacks api', () => {
[[{ module: 'environment', environments: [{}] }]],
[[null, 'text']],
])('leaves the environment unset when the token scope names none (%p)', async (scope) => {
const { api } = session({}, { items: [{ name: 'prod-token', token: 'cs-aaa', scope }] });
const { api } = session({}, { items: [{ name: 'prod-token', token: TOKEN_A, scope }] });

await expect(api.deliveryTokens('blt1')).resolves.toEqual([{ name: 'prod-token', token: 'cs-aaa', environment: undefined }]);
await expect(api.deliveryTokens('blt1')).resolves.toEqual([{ name: 'prod-token', token: TOKEN_A, environment: undefined }]);
});

it('skips a token with no value and names one by its position when it has no name', async () => {
const { api } = session(
{},
{ items: [{ name: 'no-value' }, { token: 'cs-bbb' }, { name: 'blank', token: ' ' }, null, { name: 'ok', token: 'cs-ccc' }] },
{ items: [{ name: 'no-value' }, { token: TOKEN_B }, { name: 'blank', token: ' ' }, null, { name: 'ok', token: TOKEN_C }] },
);

await expect(api.deliveryTokens('blt1')).resolves.toEqual([
{ name: 'Delivery token 2', token: 'cs-bbb', environment: undefined },
{ name: 'ok', token: 'cs-ccc', environment: undefined },
{ name: 'Delivery token 2', token: TOKEN_B, environment: undefined },
{ name: 'ok', token: TOKEN_C, environment: undefined },
]);
});

Expand Down
14 changes: 9 additions & 5 deletions src/variables/variable.create.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { randomUUID } from 'node:crypto';
import { CancelledError, UsageError } from '../core/errors';
import type { UxLike } from '../core/prompt';
import type { ServiceContext } from '../core/service-context';
import { variablesForCreate } from './variable.create';

const PICKED_TOKEN = `cs-${randomUUID()}`;
const SAVED_TOKEN = `cs-${randomUUID()}`;

const STACK = 'Import variables from a stack';
const SKIP = 'Skip adding environment variables';
const FILE = 'Import variables from a dotenv file';
Expand Down Expand Up @@ -42,7 +46,7 @@ function fakes(options: { isTTY?: boolean; answers?: Record<string, unknown>; fi
api: {
stacks: {
list: async () => [{ name: 'Shop', apiKey: 'blt1' }],
deliveryTokens: async () => [{ name: 'prod-token', token: 'cs-picked-value', environment: 'production' }],
deliveryTokens: async () => [{ name: 'prod-token', token: PICKED_TOKEN, environment: 'production' }],
},
variables: { stackTemplate: async () => [{ key: 'CONTENTSTACK_API_HOST', value: 'api.example.test' }] },
},
Expand Down Expand Up @@ -249,7 +253,7 @@ describe('variable create', () => {
it('imports through --from-stack and --alias without asking, merging it before a typed --var', async () => {
const { services, io, asked, printed } = fakes();
const savedToken = (alias: string) =>
alias === 'my-prod' ? { token: 'cs-saved-value', apiKey: 'blt9', environment: 'staging' } : undefined;
alias === 'my-prod' ? { token: SAVED_TOKEN, apiKey: 'blt9', environment: 'staging' } : undefined;

const variables = await variablesForCreate(
{ vars: ['CONTENTSTACK_ENVIRONMENT=override'], envFiles: undefined, dataDir: '/work', org: 'org1', fromStack: true, alias: 'my-prod' },
Expand All @@ -261,7 +265,7 @@ describe('variable create', () => {
expect(variables).toEqual([
{ key: 'CONTENTSTACK_API_HOST', value: 'api.example.test' },
{ key: 'CONTENTSTACK_API_KEY', value: 'blt9' },
{ key: 'CONTENTSTACK_DELIVERY_TOKEN', value: 'cs-saved-value' },
{ key: 'CONTENTSTACK_DELIVERY_TOKEN', value: SAVED_TOKEN },
{ key: 'CONTENTSTACK_ENVIRONMENT', value: 'override' },
]);
expect(asked).toEqual([]);
Expand All @@ -270,7 +274,7 @@ describe('variable create', () => {
'No value was available for CONTENTSTACK_CDN, so it was not imported.',
'\u2714 4 variables ready to set \u2014 4 from stack, CONTENTSTACK_ENVIRONMENT overridden by --var',
]);
expect(printed.join('\n')).not.toContain('cs-saved-value');
expect(printed.join('\n')).not.toContain(SAVED_TOKEN);
});

it('refuses --from-stack without --alias when there is no terminal, asking nothing', async () => {
Expand Down Expand Up @@ -308,7 +312,7 @@ describe('variable create', () => {
'EXTRA',
]);
expect(printed.join('\n')).toContain('4 from stack');
expect(printed.join('\n')).not.toContain('cs-picked-value');
expect(printed.join('\n')).not.toContain(PICKED_TOKEN);
});

it('refuses Skip combined with the stack choice', async () => {
Expand Down
9 changes: 6 additions & 3 deletions src/variables/variable.set.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { randomUUID } from 'node:crypto';
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
Expand All @@ -10,6 +11,8 @@ import { DEFAULT_VARIABLE_IO } from './variable.io';
import { VariableSetter } from './variable.set';
import type { SavedToken } from './variable.stack';

const SECRET_TOKEN = `cs-${randomUUID()}`;

const SCOPE = { org: 'org1', project: 'proj1', environment: 'env1' };

function fakes(
Expand Down Expand Up @@ -195,7 +198,7 @@ describe('variable setter with --from-stack', () => {

it('imports through the saved token under the alias for the same organization, merges it in and names the stack', async () => {
const { setter, printed, templateAsked, aliasAsked, updates } = stackSetter({
token: 'cs-secret-token',
token: SECRET_TOKEN,
apiKey: 'blt9',
environment: 'staging',
});
Expand All @@ -208,15 +211,15 @@ describe('variable setter with --from-stack', () => {
{ key: 'KEEP', value: 'k' },
{ key: 'CONTENTSTACK_API_HOST', value: 'api.example.test' },
{ key: 'CONTENTSTACK_API_KEY', value: 'blt9' },
{ key: 'CONTENTSTACK_DELIVERY_TOKEN', value: 'cs-secret-token' },
{ key: 'CONTENTSTACK_DELIVERY_TOKEN', value: SECRET_TOKEN },
{ key: 'CONTENTSTACK_ENVIRONMENT', value: 'staging' },
]);
expect(printed).toEqual([
'Using the saved delivery token "my-prod" (environment staging).',
'No value was available for CONTENTSTACK_CDN, so it was not imported.',
'\u2714 4 variables set \u2014 4 from stack',
]);
expect(printed.join('\n')).not.toContain('cs-secret-token');
expect(printed.join('\n')).not.toContain(SECRET_TOKEN);
});

it('writes nothing when the alias has no saved token', async () => {
Expand Down
43 changes: 25 additions & 18 deletions src/variables/variable.stack.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { randomUUID } from 'node:crypto';
import { configHandler } from '@contentstack/cli-utilities';

import { CancelledError, UsageError } from '../core/errors';
Expand All @@ -9,6 +10,12 @@ import type { StackTemplateEntry } from './variables.api';
import { importFromStack, readSavedToken } from './variable.stack';
import type { SavedToken } from './variable.stack';

const PROD_TOKEN = `cs-${randomUUID()}`;
const DEV_TOKEN = `cs-${randomUUID()}`;
const SAVED_TOKEN = `cs-${randomUUID()}`;
const LOOSE_TOKEN = `cs-${randomUUID()}`;
const OTHER_TOKEN = `cs-${randomUUID()}`;

const TEMPLATE: StackTemplateEntry[] = [
{ key: 'CONTENTSTACK_API_HOST', value: 'https://api.example.test' },
{ key: 'CONTENTSTACK_CDN', value: 'cdn.example.test' },
Expand All @@ -23,8 +30,8 @@ const STACKS: Stack[] = [
];

const TOKENS: DeliveryToken[] = [
{ name: 'prod-token', token: 'cs-prod-value', environment: 'production' },
{ name: 'dev-token', token: 'cs-dev-value', environment: 'development' },
{ name: 'prod-token', token: PROD_TOKEN, environment: 'production' },
{ name: 'dev-token', token: DEV_TOKEN, environment: 'development' },
];

interface Scenario {
Expand Down Expand Up @@ -88,7 +95,7 @@ function fakes(scenario: Scenario = {}) {
return { services, calls, printed, prompts };
}

const SAVED: SavedToken = { token: 'cs-saved-value', apiKey: 'blt9', environment: 'staging' };
const SAVED: SavedToken = { token: SAVED_TOKEN, apiKey: 'blt9', environment: 'staging' };

function saved(entries: Record<string, SavedToken | undefined>) {
const asked: string[] = [];
Expand All @@ -115,13 +122,13 @@ describe('variable stack import with --alias', () => {
{ key: 'CONTENTSTACK_API_HOST', value: 'api.example.test' },
{ key: 'CONTENTSTACK_CDN', value: 'cdn.example.test' },
{ key: 'CONTENTSTACK_API_KEY', value: 'blt9' },
{ key: 'CONTENTSTACK_DELIVERY_TOKEN', value: 'cs-saved-value' },
{ key: 'CONTENTSTACK_DELIVERY_TOKEN', value: SAVED_TOKEN },
{ key: 'CONTENTSTACK_ENVIRONMENT', value: 'staging' },
]);
expect(asked).toEqual(['my-prod']);
expect(calls).toEqual(['template:org1']);
expect(printed).toEqual(['Using the saved delivery token "my-prod" (environment staging).']);
expect(printed.join('\n')).not.toContain('cs-saved-value');
expect(printed.join('\n')).not.toContain(SAVED_TOKEN);
});

it('omits the host and CDN when the template carries no value for them', async () => {
Expand All @@ -144,7 +151,7 @@ describe('variable stack import with --alias', () => {
await importFromStack({ org: 'org1', alias: 'a' }, { services, savedToken });

expect(printed).toContain('No value was available for CONTENTSTACK_CDN, so it was not imported.');
expect(printed.join('\n')).not.toContain('cs-saved-value');
expect(printed.join('\n')).not.toContain(SAVED_TOKEN);
});

it('keeps a host that is not a url as it is and reduces a url to its host and port', async () => {
Expand Down Expand Up @@ -245,7 +252,7 @@ describe('variable stack import without --alias', () => {
{ key: 'CONTENTSTACK_API_HOST', value: 'api.example.test' },
{ key: 'CONTENTSTACK_CDN', value: 'cdn.example.test' },
{ key: 'CONTENTSTACK_API_KEY', value: 'blt2' },
{ key: 'CONTENTSTACK_DELIVERY_TOKEN', value: 'cs-prod-value' },
{ key: 'CONTENTSTACK_DELIVERY_TOKEN', value: PROD_TOKEN },
{ key: 'CONTENTSTACK_ENVIRONMENT', value: 'production' },
]);
expect(prompts[0].payload).toMatchObject({
Expand All @@ -262,13 +269,13 @@ describe('variable stack import without --alias', () => {
],
});
expect(printed).toEqual(['Using delivery token "prod-token" of stack "Blog" (environment production).']);
expect(JSON.stringify({ printed, prompts })).not.toContain('cs-prod-value');
expect(JSON.stringify({ printed, prompts })).not.toContain(PROD_TOKEN);
});

it('offers a token with no environment without the parenthesis and then refuses it', async () => {
const { services, prompts } = fakes({
answers: { Stack: '0', 'Delivery token': '0' },
tokens: [{ name: 'loose', token: 'cs-loose', environment: undefined }],
tokens: [{ name: 'loose', token: LOOSE_TOKEN, environment: undefined }],
});

await expect(importFromStack({ org: 'org1' }, { services, savedToken: NOTHING_SAVED })).rejects.toThrow(
Expand Down Expand Up @@ -341,40 +348,40 @@ describe('readSavedToken', () => {
}

it('reads the token csdx auth:tokens:add saved under the alias', () => {
const asked = configHolding({ 'my-prod': { token: 'cs-x', apiKey: 'blt1', environment: 'production', type: 'delivery' } });
const asked = configHolding({ 'my-prod': { token: OTHER_TOKEN, apiKey: 'blt1', environment: 'production', type: 'delivery' } });

expect(readSavedToken('my-prod')).toEqual({ token: 'cs-x', apiKey: 'blt1', environment: 'production', type: 'delivery' });
expect(readSavedToken('my-prod')).toEqual({ token: OTHER_TOKEN, apiKey: 'blt1', environment: 'production', type: 'delivery' });
expect(asked).toEqual(['tokens']);
});

it('leaves the environment and type unset when the entry has none', () => {
configHolding({ a: { token: 'cs-x', apiKey: 'blt1' } });
configHolding({ a: { token: OTHER_TOKEN, apiKey: 'blt1' } });

expect(readSavedToken('a')).toEqual({ token: 'cs-x', apiKey: 'blt1', environment: undefined, type: undefined });
expect(readSavedToken('a')).toEqual({ token: OTHER_TOKEN, apiKey: 'blt1', environment: undefined, type: undefined });
});

it.each([
[undefined],
[null],
['text'],
[{}],
[{ token: 'cs-x' }],
[{ token: OTHER_TOKEN }],
[{ apiKey: 'blt1' }],
[{ token: '', apiKey: 'blt1' }],
[{ token: 'cs-x', apiKey: ' ' }],
[{ token: OTHER_TOKEN, apiKey: ' ' }],
])('reports no saved token for an entry of %p', (entry) => {
configHolding({ a: entry });

expect(readSavedToken('a')).toBeUndefined();
});

it('finds an alias that contains a dot', () => {
configHolding({ 'prod.eu': { token: 'cs-x', apiKey: 'blt1' } });
configHolding({ 'prod.eu': { token: OTHER_TOKEN, apiKey: 'blt1' } });

expect(readSavedToken('prod.eu')).toEqual({ token: 'cs-x', apiKey: 'blt1', environment: undefined, type: undefined });
expect(readSavedToken('prod.eu')).toEqual({ token: OTHER_TOKEN, apiKey: 'blt1', environment: undefined, type: undefined });
});

it.each([[undefined], [null], ['text'], [{}], [{ other: { token: 'cs-x', apiKey: 'blt1' } }]])(
it.each([[undefined], [null], ['text'], [{}], [{ other: { token: OTHER_TOKEN, apiKey: 'blt1' } }]])(
'reports no saved token when the tokens config holds %p',
(tokens) => {
configHolding(tokens);
Expand Down
Loading