@unito/integration-cli 0.58.9 → 0.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -163,15 +163,15 @@
163
163
  },
164
164
  "grantType": {
165
165
  "type": "string",
166
- "description": "The type of grant of the OAuth 2 authorization",
167
- "enum": ["authorization_code", "password"],
168
- "tsEnumNames": ["AUTHORIZATION_CODE", "PASSWORD"]
166
+ "description": "The type of grant of the OAuth 2 authorization.",
167
+ "enum": ["authorization_code", "password", "client_credentials"],
168
+ "tsEnumNames": ["AUTHORIZATION_CODE", "PASSWORD", "CLIENT_CREDENTIALS"]
169
169
  },
170
170
  "scopes": {
171
171
  "type": "array",
172
172
  "items": {
173
173
  "type": "object",
174
- "description": "The requested scope of access to the user account",
174
+ "description": "The requested scope of access to the user account.",
175
175
  "additionalProperties": false,
176
176
  "required": ["name"],
177
177
  "properties": {
@@ -84,6 +84,16 @@
84
84
  "type": "string",
85
85
  "pattern": "^unito-secret-v[0-9]+:\/\/.*$"
86
86
  }
87
+ },
88
+ "environmentVariables": {
89
+ "type": "object",
90
+ "description": "The environment variables of the integration",
91
+ "patternProperties": {
92
+ "^[A-Z0-9_]+$": {
93
+ "type": "string"
94
+ }
95
+ },
96
+ "additionalProperties": false
87
97
  }
88
98
  }
89
99
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const core_1 = require("@oclif/core");
5
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
5
6
  const child_process_1 = tslib_1.__importDefault(require("child_process"));
6
7
  const baseCommand_1 = require("../baseCommand");
7
8
  const errors_1 = require("../errors");
@@ -81,7 +82,7 @@ class Dev extends baseCommand_1.BaseCommand {
81
82
  // Resolve the configuration.
82
83
  const environment = flags.environment ?? GlobalConfiguration.Environment.Production;
83
84
  const configuration = await (0, configuration_1.getConfiguration)(environment, flags['config-path']);
84
- let credentialPayload;
85
+ let credentialPayload = '{}';
85
86
  let readOnly = flags['read-only'] ?? false;
86
87
  if (flags['credential-id']) {
87
88
  const credential = await (0, credentials_1.fetchCredential)(environment, this.config.configDir, flags['credential-id']);
@@ -93,18 +94,28 @@ class Dev extends baseCommand_1.BaseCommand {
93
94
  credentialPayload = flags['credential-payload'];
94
95
  }
95
96
  else {
96
- let credentials = configuration.testAccounts?.[flags['test-account']];
97
+ const credentials = configuration.testAccounts?.[flags['test-account']];
97
98
  if (credentials) {
98
- credentials = (await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, credentials))
99
- .entries;
99
+ const decryptedEntries = await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, credentials);
100
+ if (decryptedEntries.failed.length) {
101
+ throw new errors_1.EntryDecryptionError(decryptedEntries.failed.at(0), environment);
102
+ }
103
+ credentialPayload = JSON.stringify(decryptedEntries.successful);
100
104
  }
101
- credentialPayload = JSON.stringify(credentials);
102
105
  }
103
- let secrets = {};
104
- // Decrypt secrets, if necessary.
105
- if (configuration.secrets) {
106
- secrets = (await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, configuration.secrets))
107
- .entries;
106
+ // Load secrets.
107
+ const { successful: secrets, failed: failedSecrets } = await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, configuration.secrets ?? {});
108
+ if (failedSecrets.length) {
109
+ throw new errors_1.EntryDecryptionError(failedSecrets.at(0), environment);
110
+ }
111
+ // Load environment variables.
112
+ const { successful: environmentVariables, failed: failedEnvironmentVariables } = await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, configuration.environmentVariables ?? {});
113
+ for (const environmentVariable of failedEnvironmentVariables) {
114
+ core_1.ux.log();
115
+ core_1.ux.log(`Could not decrypt the environment variable ${chalk_1.default.yellowBright(environmentVariable)}.`);
116
+ core_1.ux.log('It may be a normal behavior locally as some environment variables are sensitive secrets.');
117
+ core_1.ux.log(`You can still pass ${chalk_1.default.yellowBright(`${environmentVariable}=<value>`)} to the CLI command to provide a value for this environment variable.`);
118
+ core_1.ux.log(`Example: ${chalk_1.default.yellowBright(`${environmentVariable}=foo ${this.config.bin} ${this.id}`)}`);
108
119
  }
109
120
  // Launch the debugger.
110
121
  const commandArguments = [
@@ -150,7 +161,7 @@ class Dev extends baseCommand_1.BaseCommand {
150
161
  const child = child_process_1.default.spawn('node', commandArguments, {
151
162
  detached: true,
152
163
  stdio: 'inherit',
153
- env: { ...process.env, NODE_ENV: 'development', PORT: '9200' },
164
+ env: { ...process.env, ...environmentVariables, NODE_ENV: 'development', PORT: '9200' },
154
165
  });
155
166
  // istanbul ignore next
156
167
  child.on('exit', (code) => {
@@ -69,7 +69,7 @@ class Oauth2 extends core_1.Command {
69
69
  throw new errors_1.MissingAuth2AuthorizationError();
70
70
  }
71
71
  // Decrypt any encrypted oauth2 fields (mainly clientSecret)
72
- oauth2 = (await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, oauth2)).entries;
72
+ oauth2 = (await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, oauth2)).successful;
73
73
  const testAccount = flags['test-account'];
74
74
  const testAccountCredentials = configuration.testAccounts?.[testAccount];
75
75
  let credentials;
@@ -84,17 +84,17 @@ class Oauth2 extends core_1.Command {
84
84
  return;
85
85
  }
86
86
  const decryptionResult = await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, testAccountCredentials);
87
- const refreshToken = decryptionResult.entries?.refreshToken;
87
+ const refreshToken = decryptionResult.successful.refreshToken;
88
88
  core_1.ux.action.start(`Refreshing test account ${testAccount}`, undefined, { stdout: true });
89
89
  credentials = await Oauth2Resource.updateToken(oauth2, refreshToken);
90
90
  // If provider response doesn't contain a new refresh token, use the one we already have
91
91
  if (!credentials.refreshToken) {
92
92
  credentials.refreshToken = refreshToken;
93
93
  }
94
- if (decryptionResult.decryptedKeys.includes('accessToken')) {
94
+ if (decryptionResult.successful.accessToken) {
95
95
  credentials.accessToken = (await IntegrationsPlatform.encryptData(configuration.name, credentials.accessToken, false)).encryptedData;
96
96
  }
97
- if (decryptionResult.decryptedKeys.includes('refreshToken')) {
97
+ if (decryptionResult.successful.refreshToken) {
98
98
  credentials.refreshToken = (await IntegrationsPlatform.encryptData(configuration.name, credentials.refreshToken, false)).encryptedData;
99
99
  }
100
100
  core_1.ux.action.stop();
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const core_1 = require("@oclif/core");
5
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
5
6
  const child_process_1 = tslib_1.__importDefault(require("child_process"));
6
7
  const crawlerDriver_1 = require("@unito/integration-debugger/dist/src/services/crawlerDriver");
7
8
  const baseCommand_1 = require("../baseCommand");
@@ -99,7 +100,7 @@ class Test extends baseCommand_1.BaseCommand {
99
100
  // Resolve the configuration.
100
101
  const environment = flags.environment ?? GlobalConfiguration.Environment.Production;
101
102
  const configuration = await (0, configuration_1.getConfiguration)(environment, flags['config-path']);
102
- let credentialPayload;
103
+ let credentialPayload = '{}';
103
104
  let readOnly = flags['read-only'] ?? false;
104
105
  if (flags['credential-id']) {
105
106
  const credential = await (0, credentials_1.fetchCredential)(environment, this.config.configDir, flags['credential-id']);
@@ -111,18 +112,28 @@ class Test extends baseCommand_1.BaseCommand {
111
112
  credentialPayload = flags['credential-payload'];
112
113
  }
113
114
  else {
114
- let credentials = configuration.testAccounts?.[flags['test-account']];
115
+ const credentials = configuration.testAccounts?.[flags['test-account']];
115
116
  if (credentials) {
116
- credentials = (await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, credentials))
117
- .entries;
117
+ const decryptedEntries = await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, credentials);
118
+ if (decryptedEntries.failed.length) {
119
+ throw new errors_1.EntryDecryptionError(decryptedEntries.failed.at(0), environment);
120
+ }
121
+ credentialPayload = JSON.stringify(decryptedEntries.successful);
118
122
  }
119
- credentialPayload = JSON.stringify(credentials);
120
123
  }
121
- let secrets = {};
122
- // Decrypt secrets, if necessary.
123
- if (configuration.secrets) {
124
- secrets = (await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, configuration.secrets))
125
- .entries;
124
+ // Load secrets.
125
+ const { successful: secrets, failed: failedSecrets } = await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, configuration.secrets ?? {});
126
+ if (failedSecrets.length) {
127
+ throw new errors_1.EntryDecryptionError(failedSecrets.at(0), environment);
128
+ }
129
+ // Load environment variables.
130
+ const { successful: environmentVariables, failed: failedEnvironmentVariables } = await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, configuration.environmentVariables ?? {});
131
+ for (const environmentVariable of failedEnvironmentVariables) {
132
+ core_1.ux.log();
133
+ core_1.ux.log(`Could not decrypt the environment variable ${chalk_1.default.yellowBright(environmentVariable)}.`);
134
+ core_1.ux.log('It may be a normal behavior locally as some environment variables are sensitive secrets.');
135
+ core_1.ux.log(`You can still pass ${chalk_1.default.yellowBright(`${environmentVariable}=<value>`)} to the CLI command to provide a value for this environment variable.`);
136
+ core_1.ux.log(`Example: ${chalk_1.default.yellowBright(`${environmentVariable}=foo ${this.config.bin} ${this.id}`)}`);
126
137
  }
127
138
  // Launch the tests.
128
139
  const commandArguments = [
@@ -176,7 +187,7 @@ class Test extends baseCommand_1.BaseCommand {
176
187
  const child = child_process_1.default.spawn('node', commandArguments, {
177
188
  detached: true,
178
189
  stdio: 'inherit',
179
- env: { ...process.env, NODE_ENV: 'development', PORT: '9200' },
190
+ env: { ...process.env, ...environmentVariables, NODE_ENV: 'development', PORT: '9200' },
180
191
  });
181
192
  // istanbul ignore next
182
193
  child.on('exit', (code) => {
@@ -75,6 +75,16 @@ export interface Configuration {
75
75
  secrets?: {
76
76
  [k: string]: string;
77
77
  };
78
+ /**
79
+ * The environment variables of the integration
80
+ */
81
+ environmentVariables?: {
82
+ /**
83
+ * This interface was referenced by `undefined`'s JSON-Schema definition
84
+ * via the `patternProperty` "^[A-Z0-9_]+$".
85
+ */
86
+ [k: string]: string;
87
+ };
78
88
  }
79
89
  /**
80
90
  * An authorization is a way to obtain and generate credentials for an integration
@@ -180,11 +190,12 @@ export declare enum Method {
180
190
  OAUTH2 = "oauth2"
181
191
  }
182
192
  /**
183
- * The type of grant of the OAuth 2 authorization
193
+ * The type of grant of the OAuth 2 authorization.
184
194
  */
185
195
  export declare enum GrantType {
186
196
  AUTHORIZATION_CODE = "authorization_code",
187
- PASSWORD = "password"
197
+ PASSWORD = "password",
198
+ CLIENT_CREDENTIALS = "client_credentials"
188
199
  }
189
200
  /**
190
201
  * The content type of exchanged information with the provider
@@ -16,12 +16,13 @@ var Method;
16
16
  Method["OAUTH2"] = "oauth2";
17
17
  })(Method || (exports.Method = Method = {}));
18
18
  /**
19
- * The type of grant of the OAuth 2 authorization
19
+ * The type of grant of the OAuth 2 authorization.
20
20
  */
21
21
  var GrantType;
22
22
  (function (GrantType) {
23
23
  GrantType["AUTHORIZATION_CODE"] = "authorization_code";
24
24
  GrantType["PASSWORD"] = "password";
25
+ GrantType["CLIENT_CREDENTIALS"] = "client_credentials";
25
26
  })(GrantType || (exports.GrantType = GrantType = {}));
26
27
  /**
27
28
  * The content type of exchanged information with the provider
@@ -31,9 +31,8 @@ export declare class IntegrationAlreadyExistsError extends Error {
31
31
  }
32
32
  export declare class EntryDecryptionError extends Error {
33
33
  key: string;
34
- value: string;
35
34
  environment: string;
36
- constructor(key: string, value: string, targetEnvironment: string);
35
+ constructor(key: string, targetEnvironment: string);
37
36
  }
38
37
  export declare class ConfigurationInvalid extends Error {
39
38
  details: unknown;
@@ -55,12 +55,10 @@ class IntegrationAlreadyExistsError extends Error {
55
55
  exports.IntegrationAlreadyExistsError = IntegrationAlreadyExistsError;
56
56
  class EntryDecryptionError extends Error {
57
57
  key;
58
- value;
59
58
  environment;
60
- constructor(key, value, targetEnvironment) {
59
+ constructor(key, targetEnvironment) {
61
60
  super();
62
61
  this.key = key;
63
- this.value = value;
64
62
  this.environment = targetEnvironment;
65
63
  }
66
64
  }
@@ -134,7 +132,7 @@ function handleError(command, error) {
134
132
  else if (error instanceof EntryDecryptionError) {
135
133
  command.logToStderr();
136
134
  command.logToStderr(chalk_1.default.redBright('A secret cannot be decrypted!'));
137
- command.logToStderr(`The secret ${chalk_1.default.yellowBright(error.key)} with the value ${chalk_1.default.yellowBright(error.value)} could not be decrypted.`);
135
+ command.logToStderr(`The secret ${chalk_1.default.yellowBright(error.key)} could not be decrypted.`);
138
136
  command.logToStderr();
139
137
  command.logToStderr(`To decrypt this secret locally, make sure:`);
140
138
  command.logToStderr();
@@ -1,11 +1,6 @@
1
1
  import * as GlobalConfiguration from './globalConfiguration';
2
- /**
3
- * Object returned when decrypting
4
- *
5
- * Allows the caller to know which keys were decrypted to reencrypt them later as needed
6
- */
7
- export type DecryptionResult = {
8
- decryptedKeys: string[];
9
- entries: Record<string, unknown>;
2
+ export type DecryptedEntries = {
3
+ successful: Record<string, unknown>;
4
+ failed: string[];
10
5
  };
11
- export declare function decryptEntries(configurationName: string, environment: GlobalConfiguration.Environment, configDir: string, entries: Record<string, unknown>, fieldName?: string): Promise<DecryptionResult>;
6
+ export declare function decryptEntries(configurationName: string, environment: GlobalConfiguration.Environment, configDir: string, entries: Record<string, unknown>): Promise<DecryptedEntries>;
@@ -7,29 +7,37 @@ const GlobalConfiguration = tslib_1.__importStar(require("./globalConfiguration"
7
7
  const integrationsPlatform_1 = require("./integrationsPlatform");
8
8
  const configuration_1 = require("./configuration");
9
9
  const integrationsPlatform_2 = require("../services/integrationsPlatform");
10
- async function decryptEntries(configurationName, environment, configDir, entries, fieldName = 'entries') {
11
- // Get encrypted entries
12
- const encryptedEntries = Object.entries(entries).filter((entry) => typeof entry[1] === 'string' && entry[1].startsWith(configuration_1.ENCRYPTION_PREFIX));
13
- if (!encryptedEntries.length) {
14
- return { decryptedKeys: [], entries };
10
+ async function decryptEntries(configurationName, environment, configDir, entries) {
11
+ if (!Object.keys(entries).length) {
12
+ return { successful: entries, failed: [] };
13
+ }
14
+ const toDecrypt = {};
15
+ const toKeepAsIs = {};
16
+ for (const [key, value] of Object.entries(entries)) {
17
+ if (typeof value === 'string' && value.startsWith(configuration_1.ENCRYPTION_PREFIX)) {
18
+ toDecrypt[key] = value;
19
+ }
20
+ else {
21
+ toKeepAsIs[key] = value;
22
+ }
15
23
  }
16
24
  const globalConfiguration = await GlobalConfiguration.read(configDir);
17
25
  try {
18
26
  await (0, integrationsPlatform_1.validateAuthenticated)(globalConfiguration, environment);
19
27
  }
20
- catch (err) {
21
- throw new errors_1.DecryptionAuthenticationError(fieldName);
28
+ catch {
29
+ throw new errors_1.DecryptionAuthenticationError();
22
30
  }
23
- // Copy credentials to avoid mutating the configuration.
24
- const decryptedEntries = structuredClone(entries);
25
- for (const [key, encryptedEntry] of encryptedEntries) {
31
+ const decrypted = {};
32
+ const failed = [];
33
+ for (const [key, encryptedValue] of Object.entries(toDecrypt)) {
26
34
  try {
27
- decryptedEntries[key] = (await (0, integrationsPlatform_2.decryptData)(configurationName, encryptedEntry)).decryptedData;
35
+ decrypted[key] = (await (0, integrationsPlatform_2.decryptData)(configurationName, encryptedValue)).decryptedData;
28
36
  }
29
- catch (err) {
30
- throw new errors_1.EntryDecryptionError(key, encryptedEntry, environment);
37
+ catch {
38
+ failed.push(key);
31
39
  }
32
40
  }
33
- return { decryptedKeys: encryptedEntries.map(([key]) => key), entries: decryptedEntries };
41
+ return { successful: { ...toKeepAsIs, ...decrypted }, failed };
34
42
  }
35
43
  exports.decryptEntries = decryptEntries;
@@ -10,6 +10,8 @@ const IntegrationsPlatformResource = tslib_1.__importStar(require("../../src/res
10
10
  const ConfigurationResource = tslib_1.__importStar(require("../../src/resources/configuration"));
11
11
  const IntegrationResource = tslib_1.__importStar(require("../../src/resources/integrations"));
12
12
  const CredentialResource = tslib_1.__importStar(require("../../src/resources/credentials"));
13
+ const DecryptionResource = tslib_1.__importStar(require("../../src/resources/decryption"));
14
+ const styles_1 = require("../helpers/styles");
13
15
  describe('Dev', () => {
14
16
  const debuggerConfiguration = {
15
17
  ...IntegrationDebugger.getDefaultConfiguration(),
@@ -185,4 +187,22 @@ describe('Dev', () => {
185
187
  (0, test_1.expect)(spawnStub.getCall(0).args.at(1)).to.include('--credential-payload={"from":"credential"}');
186
188
  (0, test_1.expect)(spawnStub.getCall(0).args.at(1)).to.not.include('--read-only');
187
189
  });
190
+ test_1.test
191
+ .stdout()
192
+ .stub(ConfigurationResource, 'getConfiguration', stub => stub.resolves(cliConfiguration))
193
+ .stub(IntegrationResource, 'validateIsIntegrationDirectory', stub => stub.returns(true))
194
+ .stub(CredentialResource, 'fetchCredential', stub => stub.resolves({ payload: { from: 'credential' } }))
195
+ .stub(DecryptionResource, 'decryptEntries', stub => stub
196
+ .onFirstCall()
197
+ .resolves({ successful: {}, failed: [] })
198
+ .onSecondCall()
199
+ .resolves({ successful: {}, failed: [] })
200
+ .onThirdCall()
201
+ .resolves({ successful: { success: 'a' }, failed: ['failure'] }))
202
+ .command(['dev'])
203
+ .it('environment variables', ctx => {
204
+ (0, test_1.expect)(ctx.stdout).to.contain((0, styles_1.uncolorize)('Could not decrypt the environment variable failure.'));
205
+ (0, test_1.expect)(spawnStub.getCall(0).args.at(2).env.success).to.equal('a');
206
+ (0, test_1.expect)(spawnStub.getCall(0).args.at(2).env.failure).to.be.undefined;
207
+ });
188
208
  });
@@ -6,13 +6,11 @@ const core_1 = require("@oclif/core");
6
6
  const sinon = tslib_1.__importStar(require("sinon"));
7
7
  const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
8
8
  const Configuration = tslib_1.__importStar(require("../../src/resources/configuration"));
9
- const oauth2Service = tslib_1.__importStar(require("../../src/resources/oauth2"));
9
+ const Oauth2Resource = tslib_1.__importStar(require("../../src/resources/oauth2"));
10
10
  const IntegrationsPlatform = tslib_1.__importStar(require("../../src/services/integrationsPlatform"));
11
- const decryptionResource = tslib_1.__importStar(require("../../src/resources/decryption"));
11
+ const DecryptionResource = tslib_1.__importStar(require("../../src/resources/decryption"));
12
12
  const configurationTypes_1 = require("../../src/configurationTypes");
13
13
  describe('oauth2', () => {
14
- const INTEGRATION_NAME = 'myintegration';
15
- const sandbox = sinon.createSandbox();
16
14
  let getConfigurationsStub;
17
15
  let writeTestAccountStub;
18
16
  let performOAuth2FlowStub;
@@ -28,7 +26,7 @@ describe('oauth2', () => {
28
26
  grantType: configurationTypes_1.GrantType.AUTHORIZATION_CODE,
29
27
  };
30
28
  const baseConfiguration = {
31
- name: INTEGRATION_NAME,
29
+ name: 'myintegration',
32
30
  authorizations: [
33
31
  {
34
32
  name: 'MyAuthorization',
@@ -43,19 +41,18 @@ describe('oauth2', () => {
43
41
  refreshToken: 'refreshToken',
44
42
  };
45
43
  beforeEach(() => {
46
- getConfigurationsStub = sandbox.stub(Configuration, 'getConfiguration').resolves(baseConfiguration);
47
- sandbox.spy(core_1.ux.action, 'start');
48
- sandbox.spy(core_1.ux.action, 'stop');
49
- sandbox.stub(inquirer_1.default, 'prompt').resolves({
44
+ getConfigurationsStub = sinon.stub(Configuration, 'getConfiguration').resolves(baseConfiguration);
45
+ sinon.spy(core_1.ux.action, 'start');
46
+ sinon.spy(core_1.ux.action, 'stop');
47
+ sinon.stub(inquirer_1.default, 'prompt').resolves({
50
48
  oauth2Information: JSON.stringify(oauth2Information),
51
49
  });
52
- writeTestAccountStub = sandbox.stub(Configuration, 'writeTestAccount');
53
- performOAuth2FlowStub = sandbox.stub(oauth2Service, 'performOAuth2Flow');
54
- performOAuth2FlowStub.resolves(credentials);
55
- updateTokenStub = sandbox.stub(oauth2Service, 'updateToken').resolves(credentials);
50
+ writeTestAccountStub = sinon.stub(Configuration, 'writeTestAccount');
51
+ performOAuth2FlowStub = sinon.stub(Oauth2Resource, 'performOAuth2Flow').resolves(credentials);
52
+ updateTokenStub = sinon.stub(Oauth2Resource, 'updateToken').resolves(credentials);
56
53
  });
57
54
  afterEach(() => {
58
- sandbox.restore();
55
+ sinon.restore();
59
56
  });
60
57
  test_1.test
61
58
  .stdout()
@@ -81,6 +78,7 @@ describe('oauth2', () => {
81
78
  },
82
79
  ],
83
80
  }))
81
+ .stub(DecryptionResource, 'decryptEntries', stub => stub.resolves({ successful: { ...oauth2Information, clientId: 'devClientID' }, failed: [] }))
84
82
  .command(['oauth2', '--test-account', 'development'])
85
83
  .it('prioritize development authorization', () => {
86
84
  (0, test_1.expect)(performOAuth2FlowStub.getCalls().length).to.equal(1);
@@ -109,20 +107,6 @@ describe('oauth2', () => {
109
107
  'development',
110
108
  ]);
111
109
  });
112
- test_1.test
113
- .stdout()
114
- .command(['oauth2', '--test-account', 'development'])
115
- .it('performs the oauth flow when there is no test accounts and the auth information are setup', () => {
116
- (0, test_1.expect)(performOAuth2FlowStub.getCalls().length).to.equal(1);
117
- (0, test_1.expect)(updateTokenStub.getCalls().length).to.equal(0);
118
- (0, test_1.expect)(writeTestAccountStub.getCall(0).args).to.deep.equal([
119
- baseConfiguration,
120
- 'production',
121
- undefined,
122
- credentials,
123
- 'development',
124
- ]);
125
- });
126
110
  test_1.test
127
111
  .stdout()
128
112
  .do(() => getConfigurationsStub.returns({
@@ -131,6 +115,7 @@ describe('oauth2', () => {
131
115
  development: credentials,
132
116
  },
133
117
  }))
118
+ .stub(DecryptionResource, 'decryptEntries', stub => stub.resolves({ successful: { ...oauth2Information }, failed: [] }))
134
119
  .command(['oauth2', '--test-account', 'development', '--reauth'])
135
120
  .it('performs the oauth flow when there is a test accounts, the auth information are setup and --reauth flag is present', () => {
136
121
  (0, test_1.expect)(performOAuth2FlowStub.getCalls().length).to.equal(1);
@@ -156,6 +141,7 @@ describe('oauth2', () => {
156
141
  development: { something: 'something' },
157
142
  },
158
143
  }))
144
+ .stub(DecryptionResource, 'decryptEntries', stub => stub.resolves({ successful: { ...oauth2Information }, failed: [] }))
159
145
  .command(['oauth2', '--test-account', 'compliance'])
160
146
  .it('performs the oauth flow when the requested test account is not setup', () => {
161
147
  (0, test_1.expect)(performOAuth2FlowStub.getCalls().length).to.equal(1);
@@ -181,6 +167,7 @@ describe('oauth2', () => {
181
167
  development: credentials,
182
168
  },
183
169
  }))
170
+ .stub(DecryptionResource, 'decryptEntries', stub => stub.resolves({ successful: { ...oauth2Information }, failed: [] }))
184
171
  .command(['oauth2', '--test-account', 'development'])
185
172
  .it('refresh the token when there is an existing test accounts and the auth information are setup', () => {
186
173
  (0, test_1.expect)(performOAuth2FlowStub.getCalls().length).to.equal(0);
@@ -217,19 +204,19 @@ describe('oauth2', () => {
217
204
  },
218
205
  },
219
206
  });
220
- sandbox
221
- .stub(decryptionResource, 'decryptEntries')
207
+ sinon
208
+ .stub(DecryptionResource, 'decryptEntries')
222
209
  .onFirstCall()
223
- .resolves({ decryptedKeys: ['clientSecret'], entries: { ...oauth2Information, clientSecret: 'devClientID' } })
210
+ .resolves({ successful: { ...oauth2Information, clientSecret: 'devClientID' }, failed: [] })
224
211
  .onSecondCall()
225
212
  .resolves({
226
- decryptedKeys: ['accessToken', 'refreshToken'],
227
- entries: {
213
+ failed: [],
214
+ successful: {
228
215
  accessToken: 'devAccessToken',
229
216
  refreshToken: 'devRefreshToken',
230
217
  },
231
218
  });
232
- sandbox
219
+ sinon
233
220
  .stub(IntegrationsPlatform, 'encryptData')
234
221
  .onFirstCall()
235
222
  .resolves({ encryptedData: `${Configuration.ENCRYPTION_PREFIX}encryptedAccessToken` })
@@ -9,7 +9,9 @@ const IntegrationsPlatformResource = tslib_1.__importStar(require("../../src/res
9
9
  const ConfigurationResource = tslib_1.__importStar(require("../../src/resources/configuration"));
10
10
  const IntegrationResource = tslib_1.__importStar(require("../../src/resources/integrations"));
11
11
  const CredentialResource = tslib_1.__importStar(require("../../src/resources/credentials"));
12
+ const DecryptionResource = tslib_1.__importStar(require("../../src/resources/decryption"));
12
13
  const errors_1 = require("../../src/errors");
14
+ const styles_1 = require("../helpers/styles");
13
15
  describe('Test', () => {
14
16
  const cliConfiguration = {
15
17
  name: 'a',
@@ -17,6 +19,10 @@ describe('Test', () => {
17
19
  secrets: {
18
20
  secret: 'encryptedSecret',
19
21
  },
22
+ environmentVariables: {
23
+ success: '',
24
+ failure: '',
25
+ },
20
26
  testAccounts: {
21
27
  compliance: {
22
28
  accessToken: 'token',
@@ -209,4 +215,22 @@ describe('Test', () => {
209
215
  (0, test_1.expect)(spawnStub.getCall(0).args.at(1)).to.include('--credential-payload={"from":"credential"}');
210
216
  (0, test_1.expect)(spawnStub.getCall(0).args.at(1)).to.not.include('--read-only');
211
217
  });
218
+ test_1.test
219
+ .stdout()
220
+ .stub(ConfigurationResource, 'getConfiguration', stub => stub.resolves(cliConfiguration))
221
+ .stub(IntegrationResource, 'validateIsIntegrationDirectory', stub => stub.returns(true))
222
+ .stub(CredentialResource, 'fetchCredential', stub => stub.resolves({ payload: { from: 'credential' } }))
223
+ .stub(DecryptionResource, 'decryptEntries', stub => stub
224
+ .onFirstCall()
225
+ .resolves({ successful: {}, failed: [] })
226
+ .onSecondCall()
227
+ .resolves({ successful: {}, failed: [] })
228
+ .onThirdCall()
229
+ .resolves({ successful: { success: 'a' }, failed: ['failure'] }))
230
+ .command(['test'])
231
+ .it('environment variables', ctx => {
232
+ (0, test_1.expect)(ctx.stdout).to.contain((0, styles_1.uncolorize)('Could not decrypt the environment variable failure.'));
233
+ (0, test_1.expect)(spawnStub.getCall(0).args.at(2).env.success).to.equal('a');
234
+ (0, test_1.expect)(spawnStub.getCall(0).args.at(2).env.failure).to.be.undefined;
235
+ });
212
236
  });
@@ -80,7 +80,7 @@ describe('handleError', () => {
80
80
  (0, strict_1.default)(handled);
81
81
  });
82
82
  it('handles EntryDecryptionError', () => {
83
- const error = new errors_1.EntryDecryptionError('key', 'value', 'staging');
83
+ const error = new errors_1.EntryDecryptionError('key', 'staging');
84
84
  const handled = (0, errors_1.handleError)(command, error);
85
85
  (0, strict_1.default)(handled);
86
86
  });
@@ -193,6 +193,36 @@ describe('configuration', () => {
193
193
  await strict_1.default.rejects(Configuration.getConfiguration(globalConfiguration_1.Environment.Production), errors_1.ConfigurationInvalid);
194
194
  });
195
195
  });
196
+ describe('validates environment variables', () => {
197
+ it('valid', async () => {
198
+ await fs_1.default.promises.writeFile(Configuration.getConfigurationPath(), JSON.stringify({
199
+ name: 'foo',
200
+ environmentVariables: {
201
+ FOO: 'unito-secret-v1://bar',
202
+ },
203
+ }, null, 2));
204
+ const configuration = await Configuration.getConfiguration(globalConfiguration_1.Environment.Production);
205
+ strict_1.default.deepEqual(configuration.environmentVariables, { FOO: 'unito-secret-v1://bar' });
206
+ });
207
+ it('invalid - type', async () => {
208
+ await fs_1.default.promises.writeFile(Configuration.getConfigurationPath(), JSON.stringify({
209
+ name: 'foo',
210
+ environmentVariables: {
211
+ FOO: 123,
212
+ },
213
+ }, null, 2));
214
+ await strict_1.default.rejects(Configuration.getConfiguration(globalConfiguration_1.Environment.Production), errors_1.ConfigurationInvalid);
215
+ });
216
+ it('invalid - key pattern', async () => {
217
+ await fs_1.default.promises.writeFile(Configuration.getConfigurationPath(), JSON.stringify({
218
+ name: 'foo',
219
+ environmentVariables: {
220
+ foo: 'nope',
221
+ },
222
+ }, null, 2));
223
+ await strict_1.default.rejects(Configuration.getConfiguration(globalConfiguration_1.Environment.Production), errors_1.ConfigurationInvalid);
224
+ });
225
+ });
196
226
  describe('validates test accounts - custom', () => {
197
227
  it('valid test accounts', async () => {
198
228
  await fs_1.default.promises.writeFile(Configuration.getConfigurationPath(), JSON.stringify({
@@ -40,8 +40,8 @@ describe('decryption helper', () => {
40
40
  accessToken: 'developmentToken',
41
41
  accessToken1: 'developmentToken1',
42
42
  });
43
- strict_1.default.deepEqual(decryptionResult.decryptedKeys, []);
44
- strict_1.default.deepEqual(decryptionResult.entries, {
43
+ strict_1.default.deepEqual(decryptionResult.failed, []);
44
+ strict_1.default.deepEqual(decryptionResult.successful, {
45
45
  accessToken1: 'developmentToken1',
46
46
  accessToken: 'developmentToken',
47
47
  });
@@ -51,8 +51,8 @@ describe('decryption helper', () => {
51
51
  accessToken: 'token',
52
52
  encryptedToken: `${configuration_1.ENCRYPTION_PREFIX}decrypt-me`,
53
53
  });
54
- strict_1.default.deepEqual(decryptionResult.decryptedKeys, ['encryptedToken']);
55
- strict_1.default.deepEqual(decryptionResult.entries, {
54
+ strict_1.default.deepEqual(decryptionResult.failed, []);
55
+ strict_1.default.deepEqual(decryptionResult.successful, {
56
56
  accessToken: 'token',
57
57
  encryptedToken: `decrypted-me`,
58
58
  });
@@ -62,16 +62,16 @@ describe('decryption helper', () => {
62
62
  encryptedSecret1: `${configuration_1.ENCRYPTION_PREFIX}decrypt-me`,
63
63
  encryptedSecret2: `${configuration_1.ENCRYPTION_PREFIX}decrypt-me`,
64
64
  });
65
- strict_1.default.deepEqual(decryptionResult.decryptedKeys, ['encryptedSecret1', 'encryptedSecret2']);
66
- strict_1.default.deepEqual(decryptionResult.entries, {
65
+ strict_1.default.deepEqual(decryptionResult.failed, []);
66
+ strict_1.default.deepEqual(decryptionResult.successful, {
67
67
  encryptedSecret1: 'decrypted-me',
68
68
  encryptedSecret2: 'decrypted-me',
69
69
  });
70
70
  });
71
71
  it('support empty object', async () => {
72
72
  const decryptionResult = await decryptionHelper.decryptEntries(configuration.name, GlobalConfiguration.Environment.Local, '/path/to/config', {});
73
- strict_1.default.deepEqual(decryptionResult.decryptedKeys, []);
74
- strict_1.default.deepEqual(decryptionResult.entries, {});
73
+ strict_1.default.deepEqual(decryptionResult.failed, []);
74
+ strict_1.default.deepEqual(decryptionResult.successful, {});
75
75
  });
76
76
  });
77
77
  });
@@ -44,6 +44,7 @@ describe('integrations platform', function () {
44
44
  sinon.restore();
45
45
  });
46
46
  it('environment', function () {
47
+ IntegrationsPlatform.setEnvironment(globalConfiguration_1.Environment.Local);
47
48
  strict_1.default.equal(integrations_platform_client_1.default.defaults.baseUrl, integrations_platform_client_1.default.servers.local);
48
49
  IntegrationsPlatform.setEnvironment(globalConfiguration_1.Environment.Production);
49
50
  strict_1.default.equal(integrations_platform_client_1.default.defaults.baseUrl, integrations_platform_client_1.default.servers.production);
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.58.9",
2
+ "version": "0.60.0",
3
3
  "commands": {
4
4
  "activity": {
5
5
  "id": "activity",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unito/integration-cli",
3
- "version": "0.58.9",
3
+ "version": "0.60.0",
4
4
  "description": "Integration CLI",
5
5
  "bin": {
6
6
  "integration-cli": "./bin/run"