@adobe/aio-cli-plugin-app 10.2.2 → 10.3.1

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.
package/README.md CHANGED
@@ -71,7 +71,7 @@ DESCRIPTION
71
71
  Create, run, test, and deploy Adobe I/O Apps
72
72
  ```
73
73
 
74
- _See code: [src/commands/app/index.js](https://github.com/adobe/aio-cli-plugin-app/blob/10.2.2/src/commands/app/index.js)_
74
+ _See code: [src/commands/app/index.js](https://github.com/adobe/aio-cli-plugin-app/blob/10.3.1/src/commands/app/index.js)_
75
75
 
76
76
  ## `aio app add`
77
77
 
@@ -181,10 +181,12 @@ Subscribe to Services in the current Workspace
181
181
 
182
182
  ```
183
183
  USAGE
184
- $ aio app add service [-v] [--version]
184
+ $ aio app add service [-v] [--version] [--use-jwt]
185
185
 
186
186
  FLAGS
187
187
  -v, --verbose Verbose output
188
+ --use-jwt if the config has both jwt and OAuth Server to Server Credentials (while migrating), prefer the JWT
189
+ credentials
188
190
  --version Show version
189
191
 
190
192
  DESCRIPTION
@@ -372,10 +374,12 @@ Delete Services in the current Workspace
372
374
 
373
375
  ```
374
376
  USAGE
375
- $ aio app delete service [-v] [--version]
377
+ $ aio app delete service [-v] [--version] [--use-jwt]
376
378
 
377
379
  FLAGS
378
380
  -v, --verbose Verbose output
381
+ --use-jwt if the config has both jwt and OAuth Server to Server Credentials (while migrating), prefer the JWT
382
+ credentials
379
383
  --version Show version
380
384
 
381
385
  DESCRIPTION
@@ -488,7 +492,7 @@ Create a new Adobe I/O App
488
492
  ```
489
493
  USAGE
490
494
  $ aio app init [PATH] [-v] [--version] [--install] [-y] [--login] [-e <value> | -t <value>]
491
- [--standalone-app | ] [-w <value> | -i <value>] [--confirm-new-workspace]
495
+ [--standalone-app | ] [-w <value> | -i <value>] [--confirm-new-workspace] [--use-jwt]
492
496
 
493
497
  ARGUMENTS
494
498
  PATH [default: .] Path to the app directory
@@ -505,6 +509,8 @@ FLAGS
505
509
  --[no-]install [default: true] Run npm installation after files are created
506
510
  --[no-]login Login using your Adobe ID for interacting with Adobe I/O Developer Console
507
511
  --standalone-app Create a stand-alone application
512
+ --use-jwt if the config has both jwt and OAuth Server to Server Credentials (while migrating),
513
+ prefer the JWT credentials
508
514
  --version Show version
509
515
 
510
516
  DESCRIPTION
@@ -625,12 +631,14 @@ Undeploys an Adobe I/O App
625
631
 
626
632
  ```
627
633
  USAGE
628
- $ aio app undeploy [-v] [--version] [--actions] [--web-assets] [-e <value>] [--force-unpublish | --unpublish]
634
+ $ aio app undeploy [-v] [--version] [--actions] [--events] [--web-assets] [-e <value>] [--force-unpublish |
635
+ --unpublish]
629
636
 
630
637
  FLAGS
631
638
  -e, --extension=<value>... Undeploy only a specific extension, the flags can be specified multiple times
632
639
  -v, --verbose Verbose output
633
640
  --[no-]actions [default: true] Undeploy actions if any
641
+ --[no-]events [default: true] Undeploy (unregister) events if any
634
642
  --force-unpublish Force unpublish extension(s) from Exchange, will delete all extension points
635
643
  --[no-]unpublish [default: true] Unpublish selected extension(s) from Exchange
636
644
  --version Show version
@@ -647,7 +655,7 @@ Import an Adobe Developer Console configuration file.
647
655
  ```
648
656
  USAGE
649
657
  $ aio app use [CONFIG_FILE_PATH] [-v] [--version] [--overwrite | --merge] [--confirm-new-workspace] [-w
650
- <value> | [-g | -w <value>] | ] [--no-service-sync | --confirm-service-sync] [--no-input]
658
+ <value> | [-g | -w <value>] | ] [--no-service-sync | --confirm-service-sync] [--no-input] [--use-jwt]
651
659
 
652
660
  ARGUMENTS
653
661
  CONFIG_FILE_PATH path to an Adobe I/O Developer Console configuration file
@@ -670,6 +678,8 @@ FLAGS
670
678
  Workspace
671
679
  --overwrite Overwrite any .aio and .env files during import of the Adobe Developer Console
672
680
  configuration file
681
+ --use-jwt if the config has both jwt and OAuth Server to Server Credentials (while migrating),
682
+ prefer the JWT credentials
673
683
  --version Show version
674
684
 
675
685
  DESCRIPTION
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "10.2.2",
2
+ "version": "10.3.1",
3
3
  "commands": {
4
4
  "app:build": {
5
5
  "id": "app:build",
@@ -489,6 +489,12 @@
489
489
  "type": "boolean",
490
490
  "description": "Skip and confirm prompt for creating a new workspace",
491
491
  "allowNo": false
492
+ },
493
+ "use-jwt": {
494
+ "name": "use-jwt",
495
+ "type": "boolean",
496
+ "description": "if the config has both jwt and OAuth Server to Server Credentials (while migrating), prefer the JWT credentials",
497
+ "allowNo": false
492
498
  }
493
499
  },
494
500
  "args": {
@@ -809,6 +815,12 @@
809
815
  "description": "[default: true] Undeploy actions if any",
810
816
  "allowNo": true
811
817
  },
818
+ "events": {
819
+ "name": "events",
820
+ "type": "boolean",
821
+ "description": "[default: true] Undeploy (unregister) events if any",
822
+ "allowNo": true
823
+ },
812
824
  "web-assets": {
813
825
  "name": "web-assets",
814
826
  "type": "boolean",
@@ -836,6 +848,13 @@
836
848
  "exclusive": [
837
849
  "unpublish"
838
850
  ]
851
+ },
852
+ "feature-event-hooks": {
853
+ "name": "feature-event-hooks",
854
+ "type": "boolean",
855
+ "description": "[default: false] Enable event hooks feature",
856
+ "hidden": true,
857
+ "allowNo": true
839
858
  }
840
859
  },
841
860
  "args": {}
@@ -943,6 +962,12 @@
943
962
  "type": "boolean",
944
963
  "description": "Skip user prompts by setting --no-service-sync and --merge. Requires one of config_file_path or --global or --workspace",
945
964
  "allowNo": false
965
+ },
966
+ "use-jwt": {
967
+ "name": "use-jwt",
968
+ "type": "boolean",
969
+ "description": "if the config has both jwt and OAuth Server to Server Credentials (while migrating), prefer the JWT credentials",
970
+ "allowNo": false
946
971
  }
947
972
  },
948
973
  "args": {
@@ -1168,6 +1193,12 @@
1168
1193
  "type": "boolean",
1169
1194
  "description": "Show version",
1170
1195
  "allowNo": false
1196
+ },
1197
+ "use-jwt": {
1198
+ "name": "use-jwt",
1199
+ "type": "boolean",
1200
+ "description": "if the config has both jwt and OAuth Server to Server Credentials (while migrating), prefer the JWT credentials",
1201
+ "allowNo": false
1171
1202
  }
1172
1203
  },
1173
1204
  "args": {}
@@ -1454,6 +1485,12 @@
1454
1485
  "type": "boolean",
1455
1486
  "description": "Show version",
1456
1487
  "allowNo": false
1488
+ },
1489
+ "use-jwt": {
1490
+ "name": "use-jwt",
1491
+ "type": "boolean",
1492
+ "description": "if the config has both jwt and OAuth Server to Server Credentials (while migrating), prefer the JWT credentials",
1493
+ "allowNo": false
1457
1494
  }
1458
1495
  },
1459
1496
  "args": {}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@adobe/aio-cli-plugin-app",
3
3
  "description": "Create, Build and Deploy Adobe I/O Applications",
4
- "version": "10.2.2",
4
+ "version": "10.3.1",
5
5
  "author": "Adobe Inc.",
6
6
  "bugs": "https://github.com/adobe/aio-cli-plugin-app/issues",
7
7
  "dependencies": {
8
8
  "@adobe/aio-cli-lib-app-config": "^1.0.1",
9
- "@adobe/aio-cli-lib-console": "^4.0.0",
9
+ "@adobe/aio-cli-lib-console": "^4.1.0",
10
10
  "@adobe/aio-lib-core-config": "^3.0.0",
11
11
  "@adobe/aio-lib-core-logging": "^2.0.0",
12
12
  "@adobe/aio-lib-core-networking": "^4.1.0",
@@ -117,7 +117,9 @@
117
117
  },
118
118
  "integration_type": {
119
119
  "type": "string",
120
- "enum": [
120
+ "enum": [
121
+ "oauth_server_to_server",
122
+ "oauth_server_to_server_migrate",
121
123
  "oauthweb",
122
124
  "oauthios",
123
125
  "oauthandroid",
@@ -125,23 +127,61 @@
125
127
  "apikey",
126
128
  "oauthwebapp",
127
129
  "oauthnativeapp",
128
- "oauthsinglepageapp",
129
- "oauth_server_to_server"
130
+ "oauthsinglepageapp"
130
131
  ]
131
132
  },
133
+ "oauth_server_to_server": { "$ref": "#/definitions/oauth_server_to_server" },
132
134
  "jwt": { "$ref": "#/definitions/jwt" },
133
135
  "api_key": { "$ref": "#/definitions/api_key" }
134
136
  },
135
137
  "required": [ "id", "name" ],
136
- "if": { "properties": { "integration_type": { "const": "oauthsinglepageapp" } } },
137
- "then": { "properties": { "oauth2": { "$ref": "#/definitions/oauthsinglepageapp" } } },
138
- "else": { "properties": { "oauth2": { "$ref": "#/definitions/oauth2" } } },
138
+ "allOf": [
139
+ {
140
+ "if": { "properties": { "integration_type": { "const": "oauthsinglepageapp" } } },
141
+ "then": { "properties": { "oauth2": { "$ref": "#/definitions/oauthsinglepageapp" } } },
142
+ "else": { "properties": { "oauth2": { "$ref": "#/definitions/oauth2" } } }
143
+ }
144
+ ],
139
145
  "oneOf": [
140
- { "required": ["oauth2"] },
141
- { "required": ["jwt"] },
142
- { "required": ["api_key"] },
143
- { "required": ["oauth_server_to_server"] }
144
-
146
+ {
147
+ "required": ["oauth2"],
148
+ "allOf": [
149
+ { "not": { "required": ["api_key"] } },
150
+ { "not": { "required": ["jwt"] } },
151
+ { "not": { "required": ["oauth_server_to_server"] } }
152
+ ]
153
+ },
154
+ {
155
+ "required": ["jwt"],
156
+ "allOf": [
157
+ { "not": { "required": ["api_key"] } },
158
+ { "not": { "required": ["oauth2"] } },
159
+ { "not": { "required": ["oauth_server_to_server"] } }
160
+ ]
161
+ },
162
+ {
163
+ "required": ["api_key"],
164
+ "allOf": [
165
+ { "not": { "required": ["jwt"] } },
166
+ { "not": { "required": ["oauth2"] } },
167
+ { "not": { "required": ["oauth_server_to_server"] } }
168
+ ]
169
+ },
170
+ {
171
+ "required": ["oauth_server_to_server"],
172
+ "allOf": [
173
+ { "not": { "required": ["api_key"] } },
174
+ { "not": { "required": ["jwt"] } },
175
+ { "not": { "required": ["oauth2"] } }
176
+ ]
177
+ },
178
+ {
179
+ "required": ["jwt", "oauth_server_to_server"],
180
+ "allOf": [
181
+ { "not": { "required": ["api_key"] } },
182
+ { "not": { "required": ["oauth2"] } }
183
+ ]
184
+ }
145
185
  ]
146
186
  },
147
187
  "service": {
@@ -179,26 +219,6 @@
179
219
  },
180
220
  "required": [ "client_id", "client_secret", "redirect_uri", "defaultRedirectUri" ]
181
221
  },
182
- "oauth_server_to_server": {
183
- "properties": {
184
- "client_id": { "type": "string" },
185
- "client_secrets": { "type": "array" },
186
- "technical_account_email": {
187
- "type": "string",
188
- "format": "email"
189
- },
190
- "technical_account_id": {
191
- "type": "string",
192
- "format": "email"
193
- },
194
- "scopes": {
195
- "type": "array",
196
- "items": { "type": "string" },
197
- "default": []
198
- }
199
- },
200
- "required": [ "client_id", "client_secrets", "technical_account_email", "technical_account_id", "scopes" ]
201
- },
202
222
  "oauthsinglepageapp": {
203
223
  "type": "object",
204
224
  "properties": {
@@ -224,6 +244,31 @@
224
244
  },
225
245
  "required": ["client_id"]
226
246
  },
247
+ "oauth_server_to_server": {
248
+ "type": "object",
249
+ "properties": {
250
+ "client_id": { "type": "string" },
251
+ "client_secrets": {
252
+ "type": "array",
253
+ "items": { "type": "string" },
254
+ "default": []
255
+ },
256
+ "technical_account_email": {
257
+ "type": "string",
258
+ "format": "email"
259
+ },
260
+ "technical_account_id": {
261
+ "type": "string",
262
+ "format": "email"
263
+ },
264
+ "scopes": {
265
+ "type": "array",
266
+ "items": { "type": "string" },
267
+ "default": []
268
+ }
269
+ },
270
+ "required": [ "client_id", "client_secrets", "technical_account_email", "technical_account_id", "scopes" ]
271
+ },
227
272
  "jwt": {
228
273
  "type": "object",
229
274
  "properties": {
@@ -113,7 +113,7 @@ class TemplatesCommand extends AddCommand {
113
113
  type: 'table',
114
114
  name: promptName,
115
115
  bottomContent: `* = recommended by Adobe; to learn more about the templates, go to ${hyperlinker('https://adobe.ly/templates', 'https://adobe.ly/templates')}`,
116
- message: 'Choose the template(s) to install:',
116
+ message: 'Choose the template(s) to install:\n Pressing <enter> without selection will skip templates and install a standalone application.\n',
117
117
  style: { head: [], border: [] },
118
118
  wordWrap: true,
119
119
  wrapOnWordBoundary: false,
@@ -10,10 +10,12 @@ governing permissions and limitations under the License.
10
10
  */
11
11
 
12
12
  const { importConsoleConfig, downloadConsoleConfigToBuffer } = require('../../../lib/import')
13
+ const { getProjectCredentialType } = require('../../../lib/import-helper')
13
14
  const path = require('path')
14
15
  const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:add:service', { provider: 'debug' })
15
16
  const config = require('@adobe/aio-lib-core-config')
16
17
  const chalk = require('chalk')
18
+ const { Flags } = require('@oclif/core')
17
19
 
18
20
  const {
19
21
  setOrgServicesConfig,
@@ -44,16 +46,20 @@ class AddServiceCommand extends BaseCommand {
44
46
  const project = { name: projectConfig.name, id: projectConfig.id }
45
47
  const workspace = { name: projectConfig.workspace.name, id: projectConfig.workspace.id }
46
48
 
49
+ // get project credential type
50
+ const projectCredentialType = getProjectCredentialType(projectConfig, flags)
51
+
47
52
  // get latest support services
48
53
  const supportedServices = await consoleCLI.getEnabledServicesForOrg(orgId)
49
54
 
50
55
  // get current service properties
51
- const currentServiceProperties = await consoleCLI.getServicePropertiesFromWorkspace(
56
+ const currentServiceProperties = await consoleCLI.getServicePropertiesFromWorkspaceWithCredentialType({
52
57
  orgId,
53
- project.id,
58
+ projectId: project.id,
54
59
  workspace,
55
- supportedServices
56
- )
60
+ supportedServices,
61
+ credentialType: projectCredentialType
62
+ })
57
63
 
58
64
  // update the service config, subscriptions and supported services
59
65
  setOrgServicesConfig(supportedServices)
@@ -104,12 +110,14 @@ class AddServiceCommand extends BaseCommand {
104
110
  { allowCreate: false }
105
111
  )
106
112
  // get serviceProperties from source workspace
107
- newServiceProperties = await consoleCLI.getServicePropertiesFromWorkspace(
113
+ newServiceProperties = await consoleCLI.getServicePropertiesFromWorkspaceWithCredentialType({
108
114
  orgId,
109
- project.id,
110
- workspaceFrom,
111
- supportedServices
112
- )
115
+ projectId: project.id,
116
+ workspace: workspaceFrom,
117
+ supportedServices,
118
+ credentialType: projectCredentialType
119
+ })
120
+
113
121
  if (currentServiceNames.length > 0) {
114
122
  warnIfOverwriteServicesInProductionWorkspace(project.name, workspace.name)
115
123
  if (workspace.name !== 'Production') {
@@ -124,13 +132,14 @@ class AddServiceCommand extends BaseCommand {
124
132
  )
125
133
  if (confirm) {
126
134
  // if confirmed update the services
127
- await consoleCLI.subscribeToServices(
135
+ await consoleCLI.subscribeToServicesWithCredentialType({
128
136
  orgId,
129
137
  project,
130
138
  workspace,
131
- path.join(this.config.dataDir, ENTP_INT_CERTS_FOLDER),
132
- newServiceProperties
133
- )
139
+ certDir: path.join(this.config.dataDir, ENTP_INT_CERTS_FOLDER),
140
+ serviceProperties: newServiceProperties,
141
+ credentialType: projectCredentialType
142
+ })
134
143
 
135
144
  // update environment
136
145
  const config = {
@@ -154,7 +163,11 @@ AddServiceCommand.description = `Subscribe to Services in the current Workspace
154
163
  `
155
164
 
156
165
  AddServiceCommand.flags = {
157
- ...BaseCommand.flags
166
+ ...BaseCommand.flags,
167
+ 'use-jwt': Flags.boolean({
168
+ description: 'if the config has both jwt and OAuth Server to Server Credentials (while migrating), prefer the JWT credentials',
169
+ default: false
170
+ })
158
171
  }
159
172
 
160
173
  AddServiceCommand.aliases = ['app:add:services']
@@ -20,10 +20,13 @@ const {
20
20
  } = require('../../../lib/app-helper')
21
21
 
22
22
  const BaseCommand = require('../../../BaseCommand')
23
+ const { Flags } = require('@oclif/core')
23
24
 
24
- class AddServiceCommand extends BaseCommand {
25
+ const { getProjectCredentialType } = require('../../../lib/import-helper')
26
+
27
+ class DeleteServiceCommand extends BaseCommand {
25
28
  async run () {
26
- const { flags } = this.parse(AddServiceCommand)
29
+ const { flags } = await this.parse(DeleteServiceCommand)
27
30
 
28
31
  aioLogger.debug(`deleting Services in the current workspace, using flags: ${JSON.stringify(flags, null, 2)}`)
29
32
 
@@ -43,13 +46,17 @@ class AddServiceCommand extends BaseCommand {
43
46
  // get latest support services
44
47
  const supportedServices = await consoleCLI.getEnabledServicesForOrg(orgId)
45
48
 
49
+ // get credential type
50
+ const projectCredentialType = getProjectCredentialType(projectConfig, flags)
51
+
46
52
  // get current service properties
47
- const currentServiceProperties = await consoleCLI.getServicePropertiesFromWorkspace(
53
+ const currentServiceProperties = await consoleCLI.getServicePropertiesFromWorkspaceWithCredentialType({
48
54
  orgId,
49
- project.id,
55
+ projectId: project.id,
50
56
  workspace,
51
- supportedServices
52
- )
57
+ supportedServices,
58
+ credentialType: projectCredentialType
59
+ })
53
60
 
54
61
  // update the service config, subscriptions and supported services
55
62
  setOrgServicesConfig(supportedServices)
@@ -77,13 +84,14 @@ class AddServiceCommand extends BaseCommand {
77
84
  )
78
85
  if (confirm) {
79
86
  // if confirmed update the services
80
- await consoleCLI.subscribeToServices(
87
+ await consoleCLI.subscribeToServicesWithCredentialType({
81
88
  orgId,
82
89
  project,
83
90
  workspace,
84
- null, // no need to specify certDir, here we are sure that credentials are attached
85
- newServiceProperties
86
- )
91
+ certDir: null, // no need to specify certDir, here we are sure that credentials are attached
92
+ serviceProperties: newServiceProperties,
93
+ credentialType: projectCredentialType
94
+ })
87
95
  // update the service configuration with the latest subscriptions
88
96
  setWorkspaceServicesConfig(newServiceProperties)
89
97
  // success !
@@ -95,14 +103,18 @@ class AddServiceCommand extends BaseCommand {
95
103
  }
96
104
  }
97
105
 
98
- AddServiceCommand.description = `Delete Services in the current Workspace
106
+ DeleteServiceCommand.description = `Delete Services in the current Workspace
99
107
  `
100
108
 
101
- AddServiceCommand.flags = {
102
- ...BaseCommand.flags
109
+ DeleteServiceCommand.flags = {
110
+ ...BaseCommand.flags,
111
+ 'use-jwt': Flags.boolean({
112
+ description: 'if the config has both jwt and OAuth Server to Server Credentials (while migrating), prefer the JWT credentials',
113
+ default: false
114
+ })
103
115
  }
104
116
 
105
- AddServiceCommand.aliases = ['app:delete:services']
106
- AddServiceCommand.args = []
117
+ DeleteServiceCommand.aliases = ['app:delete:services']
118
+ DeleteServiceCommand.args = []
107
119
 
108
- module.exports = AddServiceCommand
120
+ module.exports = DeleteServiceCommand
@@ -21,8 +21,8 @@ const TemplateRegistryAPI = require('@adobe/aio-lib-templates')
21
21
  const inquirer = require('inquirer')
22
22
  const hyperlinker = require('hyperlinker')
23
23
 
24
- const { loadAndValidateConfigFile, importConfigJson } = require('../../lib/import-helper')
25
- const { SERVICE_API_KEY_ENV } = require('../../lib/defaults')
24
+ const { importConsoleConfig } = require('../../lib/import')
25
+ const { loadAndValidateConfigFile } = require('../../lib/import-helper')
26
26
 
27
27
  const DEFAULT_WORKSPACE = 'Stage'
28
28
 
@@ -123,7 +123,7 @@ class InitCommand extends TemplatesCommand {
123
123
 
124
124
  // 5. import config - if any
125
125
  if (flags.import) {
126
- await this.importConsoleConfig(consoleConfig)
126
+ await this.importConsoleConfig(consoleConfig, flags)
127
127
  }
128
128
  }
129
129
 
@@ -154,7 +154,7 @@ class InitCommand extends TemplatesCommand {
154
154
  await this.runCodeGenerators(this.getInitialGenerators(flags), flags.yes, consoleConfig.project.name)
155
155
 
156
156
  // 8. import config
157
- await this.importConsoleConfig(consoleConfig)
157
+ await this.importConsoleConfig(consoleConfig, flags)
158
158
 
159
159
  // 9. install templates
160
160
  await this.installTemplates({
@@ -337,20 +337,15 @@ class InitCommand extends TemplatesCommand {
337
337
  }
338
338
 
339
339
  // console config is already loaded into object
340
- async importConsoleConfig (config) {
341
- // get jwt client id
342
- const jwtConfig = config.project.workspace.details.credentials && config.project.workspace.details.credentials.find(c => c.jwt)
343
- const serviceClientId = (jwtConfig && jwtConfig.jwt.client_id) || ''
344
-
340
+ async importConsoleConfig (config, flags) {
345
341
  const configBuffer = Buffer.from(JSON.stringify(config))
346
- const interactive = false
347
- const merge = true
348
- await importConfigJson(
349
- // NOTE: importConfigJson should support reading json directly
350
- configBuffer,
351
- process.cwd(),
352
- { interactive, merge },
353
- { [SERVICE_API_KEY_ENV]: serviceClientId }
342
+ await importConsoleConfig(configBuffer,
343
+ {
344
+ interactive: false,
345
+ merge: true,
346
+ 'use-jwt': flags['use-jwt'],
347
+ skipValidation: true
348
+ }
354
349
  )
355
350
  }
356
351
  }
@@ -398,6 +393,10 @@ InitCommand.flags = {
398
393
  'confirm-new-workspace': Flags.boolean({
399
394
  description: 'Skip and confirm prompt for creating a new workspace',
400
395
  default: false
396
+ }),
397
+ 'use-jwt': Flags.boolean({
398
+ description: 'if the config has both jwt and OAuth Server to Server Credentials (while migrating), prefer the JWT credentials',
399
+ default: false
401
400
  })
402
401
  }
403
402
 
@@ -118,6 +118,14 @@ class Undeploy extends BaseCommand {
118
118
 
119
119
  try {
120
120
  await runScript(config.hooks['post-app-undeploy'])
121
+ if (flags['feature-event-hooks'] && flags.events) {
122
+ this.log('feature-event-hooks is enabled, running post-undeploy-event-reg hook')
123
+ const hookResults = await this.config.runHook('post-undeploy-event-reg', { appConfig: config })
124
+ if (hookResults?.failures?.length > 0) {
125
+ // output should be "Error : <plugin-name> : <error-message>\n" for each failure
126
+ this.error(hookResults.failures.map(f => `${f.plugin.name} : ${f.error.message}`).join('\nError: '), { exit: 1 })
127
+ }
128
+ }
121
129
  } catch (err) {
122
130
  this.log(err)
123
131
  }
@@ -147,6 +155,11 @@ Undeploy.flags = {
147
155
  default: true,
148
156
  allowNo: true
149
157
  }),
158
+ events: Flags.boolean({
159
+ description: '[default: true] Undeploy (unregister) events if any',
160
+ default: true,
161
+ allowNo: true
162
+ }),
150
163
  'web-assets': Flags.boolean({
151
164
  description: '[default: true] Undeploy web-assets if any',
152
165
  default: true,
@@ -166,6 +179,12 @@ Undeploy.flags = {
166
179
  description: 'Force unpublish extension(s) from Exchange, will delete all extension points',
167
180
  default: false,
168
181
  exclusive: ['unpublish'] // unpublish is excluded
182
+ }),
183
+ 'feature-event-hooks': Flags.boolean({
184
+ description: '[default: false] Enable event hooks feature',
185
+ default: false,
186
+ allowNo: true,
187
+ hidden: true
169
188
  })
170
189
  }
171
190
 
@@ -10,7 +10,7 @@ governing permissions and limitations under the License.
10
10
  */
11
11
 
12
12
  const BaseCommand = require('../../BaseCommand')
13
- const { CONSOLE_CONFIG_KEY } = require('../../lib/import-helper')
13
+ const { CONSOLE_CONFIG_KEY, getProjectCredentialType } = require('../../lib/import-helper')
14
14
  const { importConsoleConfig, downloadConsoleConfigToBuffer } = require('../../lib/import')
15
15
  const { Flags } = require('@oclif/core')
16
16
  const inquirer = require('inquirer')
@@ -22,7 +22,7 @@ const { ENTP_INT_CERTS_FOLDER } = require('../../lib/defaults')
22
22
  const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:use', { provider: 'debug' })
23
23
  const chalk = require('chalk')
24
24
 
25
- /* global LibConsoleCLI */
25
+ const LibConsoleCLI = require('@adobe/aio-cli-lib-console')
26
26
 
27
27
  class Use extends BaseCommand {
28
28
  async run () {
@@ -101,10 +101,12 @@ class Use extends BaseCommand {
101
101
  // get supported org services
102
102
  const supportedServices = await consoleCLI.getEnabledServicesForOrg(newConfig.org.id)
103
103
 
104
- // sync services in target workspace
104
+ // only sync services if the current configuration is complete
105
105
  if (currentConfigIsComplete) {
106
- // only sync if the current configuration is complete
107
- await this.syncServicesToTargetWorkspace(consoleCLI, prompt, currentConfig, newConfig, supportedServices, flags)
106
+ // get project credential type
107
+ const projectCredentialType = getProjectCredentialType(currentConfig, flags)
108
+
109
+ await this.syncServicesToTargetWorkspace(consoleCLI, prompt, currentConfig, newConfig, supportedServices, flags, projectCredentialType)
108
110
  }
109
111
 
110
112
  // download the console configuration for the newly selected org, project, workspace
@@ -133,7 +135,7 @@ class Use extends BaseCommand {
133
135
  const projectConfig = config.get('project') || {}
134
136
  const org = (projectConfig.org && { id: projectConfig.org.id, name: projectConfig.org.name }) || {}
135
137
  const project = { name: projectConfig.name, id: projectConfig.id }
136
- const workspace = (projectConfig.workspace && { name: projectConfig.workspace.name, id: projectConfig.workspace.id }) || {}
138
+ const workspace = (projectConfig.workspace && { ...projectConfig.workspace }) || {}
137
139
  return { org, project, workspace }
138
140
  }
139
141
 
@@ -220,24 +222,26 @@ class Use extends BaseCommand {
220
222
  * @param {LibConsoleCLI} consoleCLI lib console config
221
223
  * @private
222
224
  */
223
- async syncServicesToTargetWorkspace (consoleCLI, prompt, currentConfig, newConfig, supportedServices, flags) {
225
+ async syncServicesToTargetWorkspace (consoleCLI, prompt, currentConfig, newConfig, supportedServices, flags, projectCredentialType) {
224
226
  if (flags['no-service-sync']) {
225
227
  console.error('Skipping Services sync as \'--no-service-sync=true\'')
226
228
  console.error('Please verify Service subscriptions manually for the new Org/Project/Workspace configuration.')
227
229
  return
228
230
  }
229
- const currentServiceProperties = await consoleCLI.getServicePropertiesFromWorkspace(
230
- currentConfig.org.id,
231
- currentConfig.project.id,
232
- currentConfig.workspace,
233
- supportedServices
234
- )
235
- const serviceProperties = await consoleCLI.getServicePropertiesFromWorkspace(
236
- newConfig.org.id,
237
- newConfig.project.id,
238
- newConfig.workspace,
239
- supportedServices
240
- )
231
+ const currentServiceProperties = await consoleCLI.getServicePropertiesFromWorkspaceWithCredentialType({
232
+ orgId: currentConfig.org.id,
233
+ projectId: currentConfig.project.id,
234
+ workspace: currentConfig.workspace,
235
+ supportedServices,
236
+ credentialType: projectCredentialType
237
+ })
238
+ const serviceProperties = await consoleCLI.getServicePropertiesFromWorkspaceWithCredentialType({
239
+ orgId: newConfig.org.id,
240
+ projectId: newConfig.project.id,
241
+ workspace: newConfig.workspace,
242
+ supportedServices,
243
+ credentialType: projectCredentialType
244
+ })
241
245
 
242
246
  // service subscriptions are same
243
247
  if (this.equalSets(
@@ -298,13 +302,14 @@ class Use extends BaseCommand {
298
302
  }
299
303
  }
300
304
 
301
- await consoleCLI.subscribeToServices(
302
- newConfig.org.id,
303
- newConfig.project,
304
- newConfig.workspace,
305
- path.join(this.config.dataDir, ENTP_INT_CERTS_FOLDER),
306
- currentServiceProperties
307
- )
305
+ await consoleCLI.subscribeToServicesWithCredentialType({
306
+ orgId: newConfig.org.id,
307
+ project: newConfig.project,
308
+ workspace: newConfig.workspace,
309
+ certDir: path.join(this.config.dataDir, ENTP_INT_CERTS_FOLDER),
310
+ serviceProperties: currentServiceProperties,
311
+ credentialType: projectCredentialType
312
+ })
308
313
 
309
314
  console.error(`✔ Successfully updated Services in Project ${newConfig.project.name} and Workspace ${newConfig.workspace.name}.`)
310
315
  }
@@ -386,6 +391,10 @@ Use.flags = {
386
391
  'no-input': Flags.boolean({
387
392
  description: 'Skip user prompts by setting --no-service-sync and --merge. Requires one of config_file_path or --global or --workspace',
388
393
  default: false
394
+ }),
395
+ 'use-jwt': Flags.boolean({
396
+ description: 'if the config has both jwt and OAuth Server to Server Credentials (while migrating), prefer the JWT credentials',
397
+ default: false
389
398
  })
390
399
  }
391
400
 
@@ -19,6 +19,7 @@ const yaml = require('js-yaml')
19
19
  const hjson = require('hjson')
20
20
  const { EOL } = require('os')
21
21
  const { validateJsonWithSchema } = require('./install-helper')
22
+ const LibConsoleCLI = require('@adobe/aio-cli-lib-console')
22
23
 
23
24
  const AIO_FILE = '.aio'
24
25
  const ENV_FILE = '.env'
@@ -69,6 +70,30 @@ function loadConfigFile (fileOrBuffer) {
69
70
  return { values: {}, format: 'json' }
70
71
  }
71
72
 
73
+ /**
74
+ * Run any post-validation checks, for checks that can't be captured in JSON Schema.
75
+ *
76
+ * @private
77
+ */
78
+ // It's okay to have jwt and oauth credentials, we just default to oauth unless the user specifies to use jwt - mgoberling
79
+ //
80
+ // function postValidateChecks (configFileJson) {
81
+ // // OAuth S2S: secondary check that JSON-Schema can't handle (array item check): credentials of `integration_type`
82
+ // // "service", "oauth_server_to_server", and "oauth_server_to_server_migrate" are mutually exclusive
83
+ // const project = configFileJson.project
84
+ // const serviceIntegration = project?.workspace?.details?.credentials?.find(c => c.integration_type === 'service')
85
+ // const oauthS2SIntegration = project?.workspace?.details?.credentials?.find(c => c.integration_type === 'oauth_server_to_server')
86
+ // const oauthS2SMigrateIntegration = project?.workspace?.details?.credentials?.find(c => c.integration_type === 'oauth_server_to_server_migrate')
87
+
88
+ // if ((serviceIntegration && oauthS2SIntegration) ||
89
+ // (serviceIntegration && oauthS2SMigrateIntegration) ||
90
+ // (oauthS2SIntegration && oauthS2SMigrateIntegration)
91
+ // ) {
92
+ // const message = 'Mutually exclusive credentials: "integration_type" values: service, oauth_server_to_server, oauth_server_to_server_migrate'
93
+ // throw new Error(message)
94
+ // }
95
+ // }
96
+
72
97
  /**
73
98
  * Load and validate a config file
74
99
  *
@@ -82,6 +107,11 @@ function loadAndValidateConfigFile (fileOrBuffer) {
82
107
  const message = `Missing or invalid keys in config: ${JSON.stringify(configErrors, null, 2)}`
83
108
  throw new Error(message)
84
109
  }
110
+
111
+ // Skip post validate checks for now, it's okay to have both service and oauth
112
+ // credentials, we just default to oauth unless the user specifies to use jwt - mgoberling
113
+ // postValidateChecks(res.values)
114
+
85
115
  return res
86
116
  }
87
117
 
@@ -481,6 +511,46 @@ function transformRuntime (runtime) {
481
511
  return newRuntime
482
512
  }
483
513
 
514
+ /**
515
+ * Gets the service credential from the credentials.
516
+ *
517
+ * This is different if Jwt or OAuth Server to Server is available, and whether
518
+ * there is a migration going on from Jwt -> OAuth Server to Server.
519
+ *
520
+ * @private
521
+ * @param {object} credentials all the credentials for the workspace
522
+ * @param {boolean} useJwt prefer jwt, if available.
523
+ * @returns {object} the service credential object
524
+ */
525
+ function getServiceCredential (credentials, imsOrgId, useJwt) {
526
+ // find jwt / oauth_server_to_server credential
527
+ const jwtCredential = credentials.find(credential => typeof credential.jwt === 'object')
528
+ const oauthS2SCredential = credentials.find(credential => typeof credential.oauth_server_to_server === 'object')
529
+
530
+ // enrich jwt / oauth_server_to_server credentials with ims org id
531
+ if (jwtCredential && jwtCredential.jwt && !jwtCredential.jwt.ims_org_id) {
532
+ aioLogger.debug('adding ims_org_id to ims.jwt config')
533
+ jwtCredential.jwt.ims_org_id = imsOrgId
534
+ }
535
+
536
+ if (oauthS2SCredential && oauthS2SCredential.oauth_server_to_server && !oauthS2SCredential.oauth_server_to_server.ims_org_id) {
537
+ aioLogger.debug('adding ims_org_id to ims.oauth_server_to_server config')
538
+ oauthS2SCredential.oauth_server_to_server.ims_org_id = imsOrgId
539
+ }
540
+
541
+ if (jwtCredential && oauthS2SCredential) {
542
+ if (useJwt) {
543
+ return jwtCredential.jwt
544
+ } else {
545
+ return oauthS2SCredential.oauth_server_to_server
546
+ }
547
+ } else if (oauthS2SCredential) {
548
+ return oauthS2SCredential.oauth_server_to_server
549
+ } else if (jwtCredential) {
550
+ return jwtCredential.jwt
551
+ }
552
+ }
553
+
484
554
  /**
485
555
  * Transforms a credentials array to an object, to what this plugin expects.
486
556
  * Enrich with ims_org_id if it is a jwt credential.
@@ -508,22 +578,17 @@ function transformRuntime (runtime) {
508
578
  *
509
579
  * @param {Array} credentials array from Downloadable File Format
510
580
  * @param {string} imsOrgId the ims org id
581
+ * @param {boolean} useJwt prefer jwt credential (in in OAuth Server to Server migration scenario)
511
582
  * @returns {object} the Credentials object
512
583
  * @private
513
584
  */
514
- function transformCredentials (credentials, imsOrgId) {
515
- // find jwt credential
516
- const credential = credentials.find(credential => typeof credential.jwt === 'object')
517
-
518
- // enrich jwt credentials with ims org id
519
- if (credential && credential.jwt && !credential.jwt.ims_org_id) {
520
- aioLogger.debug('adding ims_org_id to ims.jwt config')
521
- credential.jwt.ims_org_id = imsOrgId
522
- }
585
+ function transformCredentials (credentials, imsOrgId, useJwt) {
586
+ // get jwt / oauth_server_to_server credential
587
+ const serviceCredential = getServiceCredential(credentials, imsOrgId, useJwt)
523
588
 
524
589
  return credentials.reduce((acc, credential) => {
525
- // the json schema enforces for jwt OR oauth2 OR apiKey in a credential
526
- const value = credential.oauth2 || credential.jwt || credential.api_key
590
+ // the json schema enforces for oauth2 OR apiKey OR jwt OR oauth_server_to_server in a credential
591
+ const value = credential.oauth2 || credential.api_key || serviceCredential
527
592
 
528
593
  const name = credential.name.replace(/ /gi, '_') // replace any spaces with underscores
529
594
  acc[name] = value
@@ -586,7 +651,7 @@ async function importConfigJson (configFileOrBuffer, destinationFolder = process
586
651
 
587
652
  await writeEnv({
588
653
  runtime: transformRuntime(runtime),
589
- ims: { contexts: transformCredentials(credentials, config.project.org.ims_org_id) }
654
+ ims: { contexts: transformCredentials(credentials, config.project.org.ims_org_id, flags.useJwt) }
590
655
  }, destinationFolder, flags, extraEnvVars)
591
656
 
592
657
  // remove the credentials
@@ -600,7 +665,77 @@ async function importConfigJson (configFileOrBuffer, destinationFolder = process
600
665
  return writeAio(config, destinationFolder, flags)
601
666
  }
602
667
 
668
+ /**
669
+ * Gets the service api key from the config file.
670
+ *
671
+ * This is different if Jwt or OAuth Server to Server is available, and whether
672
+ * there is a migration going on from Jwt -> OAuth Server to Server.
673
+ *
674
+ * @param {object} configFileJson the config file json
675
+ * @param {boolean} useJwt prefer the jwt credential, if available.
676
+ * @returns {string} the service api key
677
+ */
678
+ function getServiceApiKey (configFileJson, useJwt) {
679
+ const project = configFileJson?.project
680
+
681
+ const jwtConfig = project?.workspace?.details?.credentials?.find(c => c.jwt)
682
+ const oauthS2SConfig = project?.workspace?.details?.credentials?.find(c => c.oauth_server_to_server)
683
+
684
+ const jwtClientId = jwtConfig?.jwt?.client_id
685
+ const oauthS2SClientId = oauthS2SConfig?.oauth_server_to_server?.client_id
686
+
687
+ if (jwtConfig && oauthS2SConfig) {
688
+ if (useJwt) {
689
+ return jwtClientId
690
+ } else {
691
+ return oauthS2SClientId
692
+ }
693
+ } else if (oauthS2SConfig) {
694
+ return oauthS2SClientId
695
+ } else if (jwtConfig) {
696
+ return jwtClientId
697
+ }
698
+
699
+ return ''
700
+ }
701
+
702
+ /**
703
+ * Gets the service credential type from .aio data
704
+ *
705
+ * @param {object} projectConfig Project config from .aio
706
+ * @param {object} flags Command flags
707
+ * @returns {string} the credential type
708
+ */
709
+ const getProjectCredentialType = (projectConfig, flags) => {
710
+ // Note: This only looks at the first credential of each type
711
+ const jwtConfig = projectConfig?.workspace?.details?.credentials?.find(c => c.integration_type === 'service')
712
+ const oauthS2SConfig = projectConfig?.workspace?.details?.credentials?.find(c => c.integration_type === 'oauth_server_to_server')
713
+ const oauthS2SMigrateConfig = projectConfig?.workspace?.details?.credentials?.find(c => c.integration_type === 'oauth_server_to_server_migrate')
714
+
715
+ if (jwtConfig && oauthS2SConfig) {
716
+ if (flags['use-jwt']) {
717
+ return LibConsoleCLI.JWT_CREDENTIAL
718
+ } else {
719
+ return LibConsoleCLI.OAUTH_SERVER_TO_SERVER_CREDENTIAL
720
+ }
721
+ } else if (oauthS2SConfig) {
722
+ return LibConsoleCLI.OAUTH_SERVER_TO_SERVER_CREDENTIAL
723
+ } else if (oauthS2SMigrateConfig) {
724
+ if (flags['use-jwt']) {
725
+ return LibConsoleCLI.JWT_CREDENTIAL
726
+ } else {
727
+ return LibConsoleCLI.OAUTH_SERVER_TO_SERVER_CREDENTIAL
728
+ }
729
+ } else if (jwtConfig) {
730
+ return LibConsoleCLI.JWT_CREDENTIAL
731
+ }
732
+
733
+ // Default to JWT, like the other lib functions
734
+ return LibConsoleCLI.OAUTH_SERVER_TO_SERVER_CREDENTIAL
735
+ }
736
+
603
737
  module.exports = {
738
+ getServiceApiKey,
604
739
  writeFile,
605
740
  loadConfigFile,
606
741
  loadAndValidateConfigFile,
@@ -612,5 +747,6 @@ module.exports = {
612
747
  importConfigJson,
613
748
  mergeEnv,
614
749
  splitEnvLine,
750
+ getProjectCredentialType,
615
751
  CONSOLE_CONFIG_KEY
616
752
  }
package/src/lib/import.js CHANGED
@@ -1,4 +1,4 @@
1
- const { loadAndValidateConfigFile, importConfigJson } = require('./import-helper')
1
+ const { loadAndValidateConfigFile, importConfigJson, loadConfigFile, getServiceApiKey } = require('./import-helper')
2
2
  const { SERVICE_API_KEY_ENV } = require('./defaults')
3
3
 
4
4
  /**
@@ -11,20 +11,21 @@ const { SERVICE_API_KEY_ENV } = require('./defaults')
11
11
  async function importConsoleConfig (consoleConfigFileOrBuffer, flags) {
12
12
  const overwrite = flags.overwrite
13
13
  const merge = flags.merge
14
+ const skipValidation = flags.skipValidation
15
+ const useJwt = flags['use-jwt'] // for migration purposes
14
16
  let interactive = true
15
17
 
16
18
  if (overwrite || merge) {
17
19
  interactive = false
18
20
  }
19
21
 
20
- // before importing the config, first extract the service api key id
21
- const { values: config } = loadAndValidateConfigFile(consoleConfigFileOrBuffer)
22
- const project = config.project
23
- const jwtConfig = project.workspace.details.credentials && project.workspace.details.credentials.find(c => c.jwt)
24
- const serviceClientId = (jwtConfig && jwtConfig.jwt.client_id) || ''
22
+ const loadFunc = skipValidation ? loadConfigFile : loadAndValidateConfigFile
23
+ const config = loadFunc(consoleConfigFileOrBuffer).values
24
+
25
+ const serviceClientId = getServiceApiKey(config, useJwt)
25
26
  const extraEnvVars = { [SERVICE_API_KEY_ENV]: serviceClientId }
26
27
 
27
- await importConfigJson(consoleConfigFileOrBuffer, process.cwd(), { interactive, overwrite, merge }, extraEnvVars)
28
+ await importConfigJson(consoleConfigFileOrBuffer, process.cwd(), { interactive, overwrite, merge, useJwt }, extraEnvVars)
28
29
  return config
29
30
  }
30
31