@wavo-cloud/aws-secrets-manager-helper 0.1.3 → 0.1.6

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.
@@ -4,7 +4,7 @@ jobs:
4
4
 
5
5
  test:
6
6
  docker:
7
- - image: circleci/node:10.15
7
+ - image: circleci/node:12.13
8
8
 
9
9
  steps:
10
10
  - checkout
@@ -34,7 +34,7 @@ jobs:
34
34
 
35
35
  module-push:
36
36
  docker:
37
- - image: circleci/node:10.15
37
+ - image: circleci/node:12.13
38
38
 
39
39
  steps:
40
40
  - checkout
package/.eslintrc ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": ["@wavo-cloud/eslint-config"],
3
+ "rules": {
4
+ // You can disable rules here
5
+ }
6
+ }
package/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM node:10.15-alpine
1
+ FROM node:12.13-alpine
2
2
 
3
3
  # Create app directory
4
4
  RUN mkdir -p /usr/local/src/cloud-app
package/app/index.js CHANGED
@@ -1,2 +1 @@
1
- 'use strict'
2
1
  module.exports = require('./utils/awsSecretsManager')
@@ -1,12 +1,16 @@
1
1
  const AWS = require('aws-sdk')
2
+ const { merge, omit } = require('lodash')
2
3
 
3
- const region = 'us-east-1'
4
+ const secretsManagerRegion = 'us-east-1'
4
5
  const clientIdsSecretId = 'wavo/self_serve/client_list'
5
6
  const client = new AWS.SecretsManager({
6
- region,
7
+ region: secretsManagerRegion,
7
8
  })
8
9
 
9
- // attempts to parse secret
10
+ /**
11
+ * Attempts to parse secret
12
+ * @param {String} secretString - The stringified secret
13
+ */
10
14
  async function parseSecret(secretString) {
11
15
  let parsedSecret = {}
12
16
  try {
@@ -18,8 +22,35 @@ async function parseSecret(secretString) {
18
22
  }
19
23
  }
20
24
 
21
- // gets a defined secret value from aws secrets manager
22
- // returns JSON where JSON is the secret
25
+ function handleAWSPromiseError(promiseError) {
26
+ if (promiseError.code === 'DecryptionFailureException') {
27
+ promiseError.message =
28
+ "Secrets Manager can't decrypt the protected secret text using the provided KMS key."
29
+ } else if (promiseError.code === 'InternalServiceErrorException') {
30
+ promiseError.message = 'An error occurred on the server side.'
31
+ } else if (promiseError.code === 'InvalidParameterException') {
32
+ promiseError.message = 'You provided an invalid value for a parameter.'
33
+ } else if (promiseError.code === 'InvalidRequestException') {
34
+ promiseError.message =
35
+ 'You provided a parameter value that is not valid for the current state of the resource.'
36
+ } else if (promiseError.code === 'ResourceNotFoundException') {
37
+ promiseError.message = "We can't find the resource that you asked for."
38
+ } else if (promiseError.code === 'EncryptionFailureException') {
39
+ promiseError.message =
40
+ 'We could not encrypt your secret. Your KMS/CMK key may be invalid.'
41
+ } else if (promiseError.code === 'LimitExceededException') {
42
+ promiseError.message = 'Your request would exceed our AWSSM internal limit.'
43
+ } else if (promiseError.code === 'ResourceExistsException') {
44
+ promiseError.message = 'A resource with this identifier already exists.'
45
+ }
46
+ throw promiseError
47
+ }
48
+
49
+ /**
50
+ * Gets a defined secret value from aws secrets manager
51
+ * Returns JSON where JSON is the secret
52
+ * @param {String} secretId - The ID of an existing secret
53
+ */
23
54
  async function getSecretValue(secretId) {
24
55
  let promiseError
25
56
  const data = await client
@@ -30,24 +61,7 @@ async function getSecretValue(secretId) {
30
61
  })
31
62
 
32
63
  if (promiseError) {
33
- if (promiseError.code === 'DecryptionFailureException') {
34
- promiseError.message =
35
- "Secrets Manager can't decrypt the protected secret text using the provided KMS key."
36
- throw promiseError
37
- } else if (promiseError.code === 'InternalServiceErrorException') {
38
- promiseError.message = 'An error occurred on the server side.'
39
- throw promiseError
40
- } else if (promiseError.code === 'InvalidParameterException') {
41
- promiseError.message = 'You provided an invalid value for a parameter.'
42
- throw promiseError
43
- } else if (promiseError.code === 'InvalidRequestException') {
44
- promiseError.message =
45
- 'You provided a parameter value that is not valid for the current state of the resource.'
46
- throw promiseError
47
- } else if (promiseError.code === 'ResourceNotFoundException') {
48
- promiseError.message = "We can't find the resource that you asked for."
49
- throw promiseError
50
- }
64
+ handleAWSPromiseError(promiseError)
51
65
  } else if ('SecretString' in data) {
52
66
  return parseSecret(data.SecretString)
53
67
  } else {
@@ -56,28 +70,32 @@ async function getSecretValue(secretId) {
56
70
  }
57
71
  }
58
72
 
59
- // gets the secret keying where client secrets are stored
60
- // returns JSON where JSON is the secret
61
- async function getClientSecretIds() {
62
- return getSecretValue(clientIdsSecretId)
73
+ /**
74
+ * Gets the secret keying where client secrets are stored
75
+ * Returns JSON where JSON is the secret
76
+ */
77
+ async function getClientSecretIds(clientIdsSecretIdOverride = null) {
78
+ return getSecretValue(clientIdsSecretIdOverride || clientIdsSecretId)
63
79
  }
64
80
 
65
- // gets a specific client secret holding their API keys
66
- // returns JSON where JSON is the secret
81
+ /**
82
+ * Gets a specific client secret holding their API keys
83
+ * Returns JSON where JSON is the secret
84
+ * @param {String} clientId - The client ID
85
+ */
67
86
  async function getClientSecret(clientId) {
68
87
  const clientSecretIds = await getClientSecretIds()
69
88
  return getSecretValue(clientSecretIds[clientId])
70
89
  }
71
90
 
72
- // gets all client secrets holding their API keys
73
- // returns [{ clientId: String, secretId: String, ...secret }]
74
- // skips all that are missing a region or organization
91
+ /**
92
+ * Gets all client secrets holding their API keys
93
+ * Returns [{ clientId: String, secretId: String, ...secret }]
94
+ * Skips all that are missing a region or organization
95
+ */
75
96
  async function getAllClientSecrets() {
76
97
  const clientSecretIds = await getClientSecretIds()
77
- if (!clientSecretIds) {
78
- console.log('No clients found')
79
- return []
80
- }
98
+ if (!clientSecretIds) return []
81
99
  const parsedClientSecrets = await Promise.all(
82
100
  Object.keys(clientSecretIds).map(async clientId => {
83
101
  const result = await getSecretValue(clientSecretIds[clientId])
@@ -89,16 +107,218 @@ async function getAllClientSecrets() {
89
107
  return parsedClientSecrets.filter(clientSecret => {
90
108
  if (clientSecret.organization && clientSecret.region && !clientSecret.error)
91
109
  return true
92
- console.log(
93
- `Client secret at ${clientSecret.secretId} is missing organization or region`
94
- )
95
110
  return false
96
111
  })
97
112
  }
98
113
 
114
+ /**
115
+ * Creates a new secret
116
+ * @param {String} secretName - The friendly name of the secret
117
+ * @param {String} secretDescription - The description of the secret
118
+ * @param {String} secretValue - The JSON string secret value
119
+ */
120
+ async function createSecret(secretName, secretDescription, secretValue) {
121
+ let promiseError
122
+ if (typeof secretValue !== 'string')
123
+ throw new Error('Secret value must be a JSON string.')
124
+ const regex = new RegExp('^[a-z0-9-_/]*$')
125
+ if (!regex.test(secretName))
126
+ throw new Error(
127
+ 'The secret name can only include lowercase alphanumerics, underscores, and dashes.'
128
+ )
129
+ const data = await client
130
+ .createSecret({
131
+ Name: secretName,
132
+ Description: secretDescription,
133
+ SecretString: secretValue,
134
+ })
135
+ .promise()
136
+ .catch(error => {
137
+ promiseError = error
138
+ })
139
+
140
+ if (promiseError) handleAWSPromiseError(promiseError)
141
+ else return data
142
+ }
143
+
144
+ /**
145
+ * Edits an existing secret
146
+ * @param {String} secretName - The friendly name of the secret
147
+ * @param {Object} secretValue - The new JSON string secret value
148
+ */
149
+ async function editSecret(secretName, secretValue) {
150
+ let promiseError
151
+ if (typeof secretValue !== 'string')
152
+ throw new Error('Secret value must be a JSON string.')
153
+ const data = await client
154
+ .updateSecret({
155
+ SecretId: secretName,
156
+ SecretString: secretValue,
157
+ })
158
+ .promise()
159
+ .catch(error => {
160
+ promiseError = error
161
+ })
162
+
163
+ if (promiseError) handleAWSPromiseError(promiseError)
164
+ else return data
165
+ }
166
+
167
+ /**
168
+ * Adds a client to the client list and does data validation
169
+ * @param {String} clientId - The ID of the client
170
+ * @param {String} clientName - The display name of the client
171
+ * @param {String} organization - The location of the organization's data in S3
172
+ * @param {String} region - The sub location of the organization's data in S3
173
+ * @param {Object} keyValuePairs - The secret to store for the client
174
+ */
175
+ async function createClient(
176
+ clientId,
177
+ clientName,
178
+ organization,
179
+ region,
180
+ keyValuePairs,
181
+ clientIdsSecretIdOverride = null
182
+ ) {
183
+ if (!clientId || !clientName || !organization || !region)
184
+ throw new Error('Client ID, name, organization, and region are required.')
185
+ const regex = new RegExp('^[a-z0-9-_]*$')
186
+ if (!regex.test(organization) || !regex.test(region))
187
+ throw new Error(
188
+ 'Organization and region can only include lowercase alphanumerics, underscores, and dashes.'
189
+ )
190
+ // get client list secret
191
+ const currentClientList = await getClientSecretIds(
192
+ clientIdsSecretIdOverride || clientIdsSecretId
193
+ )
194
+ // if client is already in list, throw error
195
+ if (currentClientList[clientId])
196
+ throw new Error('A client with this ID already exists.')
197
+ // create client secret
198
+ const createSecretResults = await createSecret(
199
+ `${organization}/ad_platforms/api`,
200
+ `${clientName} Ad Platform API Keys`,
201
+ JSON.stringify({
202
+ client_name: clientName,
203
+ organization,
204
+ region,
205
+ ...keyValuePairs,
206
+ })
207
+ )
208
+ // add to client list
209
+ const editClientListResults = await editSecret(
210
+ clientIdsSecretIdOverride || clientIdsSecretId,
211
+ JSON.stringify({
212
+ [clientId]: `${organization}/ad_platforms/api`,
213
+ ...currentClientList,
214
+ })
215
+ )
216
+
217
+ return { createSecretResults, editClientListResults }
218
+ }
219
+
220
+ /**
221
+ * Directly sets the secret value of an existing secret
222
+ * @param {String} clientId - The ID of the client
223
+ * @param {String} clientName - The display name of the client
224
+ * @param {String} organization - The location of the organization's data in S3
225
+ * @param {String} region - The sub location of the organization's data in S3
226
+ * @param {Object} keyValuePairs - The secret to store for the client
227
+ */
228
+ async function setClient(
229
+ clientId,
230
+ clientName,
231
+ organization,
232
+ region,
233
+ keyValuePairs,
234
+ clientIdsSecretIdOverride = null
235
+ ) {
236
+ if (!clientId || !clientName || !organization || !region)
237
+ throw new Error('Client ID, name, organization, and region are required.')
238
+ const regex = new RegExp('^[a-z0-9-_]*$')
239
+ if (
240
+ (organization && !regex.test(organization)) ||
241
+ (region && !regex.test(region))
242
+ )
243
+ throw new Error(
244
+ 'Organization and region can only include lowercase alphanumerics, underscores, and dashes.'
245
+ )
246
+ // get client secret location
247
+ const currentClientList = await getClientSecretIds(clientIdsSecretIdOverride)
248
+ const clientSecretId = currentClientList[clientId]
249
+ // set client
250
+ const setClientResults = await editSecret(
251
+ clientSecretId,
252
+ JSON.stringify({
253
+ client_name: clientName,
254
+ organization,
255
+ region,
256
+ ...keyValuePairs,
257
+ })
258
+ )
259
+
260
+ return { setClientResults }
261
+ }
262
+
263
+ /**
264
+ * Edits a secret value of an existing secret
265
+ * It merges keyValuePairsToAdd
266
+ * It omits keysToDelete
267
+ * @param {String} clientId - The ID of the client
268
+ * @param {String} clientName - The display name of the client
269
+ * @param {String} organization - The location of the organization's data in S3
270
+ * @param {String} region - The sub location of the organization's data in S3
271
+ * @param {Object} keyValuePairsToAdd
272
+ * @param {Array} keysToDelete
273
+ */
274
+ async function editClient(
275
+ clientId,
276
+ keyValuePairsToAdd,
277
+ keysToDelete,
278
+ clientIdsSecretIdOverride
279
+ ) {
280
+ if (!clientId) throw new Error('Client ID is required.')
281
+ if (
282
+ keysToDelete.some(key =>
283
+ ['organization', 'region', 'client_name'].includes(key)
284
+ )
285
+ )
286
+ throw new Error('You cannot unset the organization, region, or client_name')
287
+ const regex = new RegExp('^[a-z0-9-_]*$')
288
+ const keysToAdd = Object.keys(keyValuePairsToAdd)
289
+ if (
290
+ (keysToAdd.includes('organization') &&
291
+ (!regex.test(keyValuePairsToAdd.organization) ||
292
+ !keyValuePairsToAdd.organization)) ||
293
+ (keysToAdd.includes('region') &&
294
+ (!regex.test(keyValuePairsToAdd.region) || !keyValuePairsToAdd.region)) ||
295
+ (keysToAdd.includes('client_name') && !keyValuePairsToAdd.client_name)
296
+ )
297
+ throw new Error(
298
+ 'Organization, region, and client_name can only include lowercase alphanumerics, underscores, and dashes and must be non-null if included.'
299
+ )
300
+ // get client secret location
301
+ const currentClientList = await getClientSecretIds(clientIdsSecretIdOverride)
302
+ const clientSecretId = currentClientList[clientId]
303
+ const keyValuePairs = await getSecretValue(clientSecretId)
304
+ merge(keyValuePairs, keyValuePairsToAdd)
305
+ // edit client
306
+ const editClientResults = await editSecret(
307
+ clientSecretId,
308
+ JSON.stringify({
309
+ ...omit(keyValuePairs, keysToDelete),
310
+ })
311
+ )
312
+
313
+ return { editClientResults }
314
+ }
315
+
99
316
  module.exports = {
100
317
  getClientSecretIds,
101
318
  getClientSecret,
102
319
  getAllClientSecrets,
103
320
  getSecretValue,
321
+ createClient,
322
+ setClient,
323
+ editClient,
104
324
  }
@@ -0,0 +1,108 @@
1
+ const AWS = require('aws-sdk')
2
+ const { omit } = require('lodash')
3
+ const { getClientSecretIds } = require('./awsSecretsManager')
4
+
5
+ const secretsManagerRegion = 'us-east-1'
6
+ const clientIdsSecretId = 'wavo/self_serve/client_list'
7
+ const client = new AWS.SecretsManager({
8
+ region: secretsManagerRegion,
9
+ })
10
+
11
+ function handleAWSPromiseError(promiseError) {
12
+ if (promiseError.code === 'DecryptionFailureException') {
13
+ promiseError.message =
14
+ "Secrets Manager can't decrypt the protected secret text using the provided KMS key."
15
+ } else if (promiseError.code === 'InternalServiceErrorException') {
16
+ promiseError.message = 'An error occurred on the server side.'
17
+ } else if (promiseError.code === 'InvalidParameterException') {
18
+ promiseError.message = 'You provided an invalid value for a parameter.'
19
+ } else if (promiseError.code === 'InvalidRequestException') {
20
+ promiseError.message =
21
+ 'You provided a parameter value that is not valid for the current state of the resource.'
22
+ } else if (promiseError.code === 'ResourceNotFoundException') {
23
+ promiseError.message = "We can't find the resource that you asked for."
24
+ } else if (promiseError.code === 'EncryptionFailureException') {
25
+ promiseError.message =
26
+ 'We could not encrypt your secret. Your KMS/CMK key may be invalid.'
27
+ } else if (promiseError.code === 'LimitExceededException') {
28
+ promiseError.message = 'Your request would exceed our AWSSM internal limit.'
29
+ } else if (promiseError.code === 'ResourceExistsException') {
30
+ promiseError.message = 'A resource with this identifier already exists.'
31
+ }
32
+ throw promiseError
33
+ }
34
+
35
+ /**
36
+ * Edits an existing secret
37
+ * @param {String} secretName - The friendly name of the secret
38
+ * @param {Object} secretValue - The new JSON string secret value
39
+ */
40
+ async function editSecret(secretName, secretValue) {
41
+ let promiseError
42
+ if (typeof secretValue !== 'string')
43
+ throw new Error('Secret value must be a JSON string.')
44
+ const data = await client
45
+ .updateSecret({
46
+ SecretId: secretName,
47
+ SecretString: secretValue,
48
+ })
49
+ .promise()
50
+ .catch(error => {
51
+ promiseError = error
52
+ })
53
+
54
+ if (promiseError) handleAWSPromiseError(promiseError)
55
+ else return data
56
+ }
57
+
58
+ /**
59
+ * Delete a secret
60
+ * @param {String} secretId - The ID of the secret
61
+ * @param {Boolean} force - If true, the secret cannot be recovered
62
+ * @param {Number} recoveryWindow - The recovery window
63
+ */
64
+ async function deleteSecret(secretId, force = false, recoveryWindow = 30) {
65
+ let promiseError
66
+ if (!secretId) throw new Error('Secret value must be a JSON string.')
67
+
68
+ const data = await client
69
+ .deleteSecret({
70
+ ForceDeleteWithoutRecovery: force,
71
+ RecoveryWindowInDays: force ? undefined : recoveryWindow,
72
+ SecretId: secretId,
73
+ })
74
+ .promise()
75
+ .catch(error => {
76
+ promiseError = error
77
+ })
78
+
79
+ if (promiseError) handleAWSPromiseError(promiseError)
80
+ else return data
81
+ }
82
+
83
+ async function deleteClient(
84
+ clientId,
85
+ force,
86
+ recoveryWindow,
87
+ clientIdsSecretIdOverride = null,
88
+ ) {
89
+ if (!clientId) throw new Error('Client ID is required')
90
+ // get current client list
91
+ const currentClientList = await getClientSecretIds(clientIdsSecretIdOverride)
92
+ const secretId = currentClientList[clientId]
93
+ if (!secretId)
94
+ throw new Error('The client ID could not be found or is invalid')
95
+ // delete the client secret
96
+ const deleteSecretResult = await deleteSecret(secretId, force, recoveryWindow)
97
+ // remove them from client list
98
+ const editClientListResult = await editSecret(
99
+ clientIdsSecretIdOverride || clientIdsSecretId,
100
+ JSON.stringify(omit(currentClientList, [clientId]))
101
+ )
102
+
103
+ return { deleteSecretResult, editClientListResult }
104
+ }
105
+
106
+ module.exports = {
107
+ deleteClient,
108
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wavo-cloud/aws-secrets-manager-helper",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "description": "Wavo Cloud Infallible AWS Secrets Manager Helper",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -24,18 +24,18 @@
24
24
  "ci-test": "yarn docker-test"
25
25
  },
26
26
  "devDependencies": {
27
+ "@wavo-cloud/eslint-config": "^0.0.9",
27
28
  "@wavo-cloud/generator-microservice": "^2.5.0",
28
29
  "chai": "^4.2.0",
29
- "eslint": "^5.6.0",
30
+ "eslint": "^7.6.0",
30
31
  "eslint-config-prettier": "^3.1.0",
31
32
  "mocha": "^5.2.0",
32
- "mock-local-storage": "^1.1.7",
33
- "prettier": "^1.14.3",
34
- "webpack": "^4.27.1",
35
- "webpack-cli": "^3.1.2"
33
+ "mock-local-storage": "^1.1.12",
34
+ "prettier": "^1.14.3"
36
35
  },
37
36
  "dependencies": {
38
- "aws-sdk": "^2.713.0"
37
+ "aws-sdk": "^2.713.0",
38
+ "lodash": "^4.17.19"
39
39
  },
40
40
  "bugs": {
41
41
  "url": "https://github.com/Wavo/wavo-cloud.aws-secrets-manager-helper/issues"
@@ -49,5 +49,8 @@
49
49
  "secrets",
50
50
  "manager"
51
51
  ],
52
- "author": "Zain Virani"
52
+ "author": "Zain Virani",
53
+ "engines": {
54
+ "node": ">=12.13"
55
+ }
53
56
  }
package/test/aws.test.js CHANGED
@@ -1,8 +1,17 @@
1
+ /* eslint-disable */
1
2
  const {
2
3
  getAllClientSecrets,
4
+ getClientSecretIds,
5
+ getSecretValue,
6
+ createClient,
7
+ setClient,
8
+ editClient,
3
9
  } = require('../app/utils/awsSecretsManager')
10
+ const { deleteClient } = require('../app/utils/awsSecretsManagerDeleteHelper')
4
11
  const expect = require('chai').expect
5
12
 
13
+ const clientIdsSecretId = 'wavo/self_serve/client_list_test'
14
+
6
15
  describe('Test Secrets Manager Helper', async () => {
7
16
  /**
8
17
  * This function call tests all of the helper functions
@@ -14,4 +23,89 @@ describe('Test Secrets Manager Helper', async () => {
14
23
  expect(clientSecrets[0].clientId).to.not.be.null
15
24
  expect(clientSecrets[0].secretId).to.not.be.null
16
25
  })
17
- })
26
+
27
+ it('should create a new client', async () => {
28
+ const createClientResult = await createClient(
29
+ 'test_client_id',
30
+ 'test_client_name',
31
+ 'test_organization',
32
+ 'test_region',
33
+ { test_secret_key: 'test_secret_value' },
34
+ clientIdsSecretId
35
+ )
36
+ expect(createClientResult.createSecretResults.Name).to.equal(
37
+ 'test_organization/ad_platforms/api'
38
+ )
39
+
40
+ const clientList = await getClientSecretIds(clientIdsSecretId)
41
+ expect(clientList['test_client_id']).to.equal(
42
+ 'test_organization/ad_platforms/api'
43
+ )
44
+ })
45
+
46
+ it('should set a client', async () => {
47
+ await setClient(
48
+ 'test_client_id',
49
+ 'test_client_name_2',
50
+ 'test_organization',
51
+ 'test_region_new',
52
+ { test_secret_key_new: 'test_secret_value_new', to_delete: 'to_delete' },
53
+ clientIdsSecretId
54
+ )
55
+
56
+ const testSecret = await getSecretValue(
57
+ 'test_organization/ad_platforms/api'
58
+ )
59
+ expect(testSecret.client_name).to.equal('test_client_name_2')
60
+ expect(testSecret.organization).to.equal('test_organization')
61
+ expect(testSecret.region).to.equal('test_region_new')
62
+ expect(testSecret.test_secret_key).to.equal(undefined)
63
+ expect(testSecret.test_secret_key_new).to.equal('test_secret_value_new')
64
+ expect(testSecret.to_delete).to.equal('to_delete')
65
+ })
66
+
67
+ it('should edit, add, and delete a client key/value secret', async () => {
68
+ await editClient(
69
+ 'test_client_id',
70
+ {
71
+ test_secret_key_new: 'edit',
72
+ new_key: 'add',
73
+ organization: 'test_organization_new'
74
+ },
75
+ ['to_delete'],
76
+ clientIdsSecretId
77
+ )
78
+
79
+ const testSecret = await getSecretValue(
80
+ 'test_organization/ad_platforms/api'
81
+ )
82
+
83
+ expect(testSecret.client_name).to.equal('test_client_name_2')
84
+ expect(testSecret.organization).to.equal('test_organization_new')
85
+ expect(testSecret.region).to.equal('test_region_new')
86
+ expect(testSecret.test_secret_key_new).to.equal('edit')
87
+ expect(testSecret.new_key).to.equal('add')
88
+ expect(testSecret.to_delete).to.equal(undefined)
89
+ })
90
+
91
+ it('should delete a client', async () => {
92
+ //let clientList
93
+ await deleteClient(
94
+ 'test_client_id',
95
+ true,
96
+ 30,
97
+ clientIdsSecretId,
98
+ )
99
+
100
+ const clientList = await getClientSecretIds(clientIdsSecretId)
101
+
102
+ try {
103
+ await getSecretValue(
104
+ 'test_organization/ad_platforms/api'
105
+ )
106
+ expect.fail("'test_organization/ad_platforms/api' should not be a valid secret value")
107
+ } catch (error) {
108
+ expect(clientList['test_client_id']).to.equal(undefined)
109
+ }
110
+ })
111
+ })