@adobe/aio-cli-plugin-app-storage 1.0.3 → 1.2.0-pre.2025-11-18.sha-9c4079ac
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 +676 -12
- package/package.json +33 -6
- package/src/BaseCommand.js +11 -58
- package/src/DBBaseCommand.js +112 -0
- package/src/StateBaseCommand.js +87 -0
- package/src/commands/app/add/db.js +20 -0
- package/src/commands/app/db/collection/create.js +119 -0
- package/src/commands/app/db/collection/drop.js +90 -0
- package/src/commands/app/db/collection/list.js +100 -0
- package/src/commands/app/db/collection/rename.js +98 -0
- package/src/commands/app/db/collection/stats.js +94 -0
- package/src/commands/app/db/delete.js +89 -0
- package/src/commands/app/db/document/count.js +96 -0
- package/src/commands/app/db/document/delete.js +95 -0
- package/src/commands/app/db/document/find.js +133 -0
- package/src/commands/app/db/document/insert.js +147 -0
- package/src/commands/app/db/document/replace.js +122 -0
- package/src/commands/app/db/document/update.js +144 -0
- package/src/commands/app/db/index/create.js +170 -0
- package/src/commands/app/db/index/drop.js +87 -0
- package/src/commands/app/db/index/list.js +82 -0
- package/src/commands/app/db/ping.js +77 -0
- package/src/commands/app/db/provision.js +190 -0
- package/src/commands/app/db/stats.js +87 -0
- package/src/commands/app/db/status.js +159 -0
- package/src/commands/app/state/delete.js +3 -3
- package/src/commands/app/state/get.js +2 -2
- package/src/commands/app/state/list.js +3 -3
- package/src/commands/app/state/put.js +4 -4
- package/src/commands/app/state/stats.js +2 -2
- package/src/constants/db.js +32 -0
- package/src/constants/global.js +14 -0
- package/src/{constants.js → constants/state.js} +3 -0
- package/src/utils/inputValidation.js +74 -0
- package/src/utils/output.js +35 -0
- package/oclif.manifest.json +0 -306
package/package.json
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/aio-cli-plugin-app-storage",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.2.0-pre.2025-11-18.sha-9c4079ac",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@adobe/aio-lib-core-config": "^5",
|
|
6
6
|
"@adobe/aio-lib-core-logging": "^3",
|
|
7
|
+
"@adobe/aio-lib-db": "^0.1.0-beta.2",
|
|
8
|
+
"@adobe/aio-lib-env": "^3.0.1",
|
|
7
9
|
"@adobe/aio-lib-state": "^5",
|
|
8
10
|
"@inquirer/prompts": "^5",
|
|
9
11
|
"@oclif/core": "^4",
|
|
10
12
|
"@oclif/plugin-help": "^6",
|
|
13
|
+
"@oclif/table": "^0.5.0",
|
|
11
14
|
"chalk": "^5",
|
|
15
|
+
"dotenv": "^16.5.0",
|
|
12
16
|
"semver": "^7.6.3"
|
|
13
17
|
},
|
|
14
18
|
"devDependencies": {
|
|
15
|
-
"@adobe/eslint-config-aio-lib-config": "^4",
|
|
19
|
+
"@adobe/eslint-config-aio-lib-config": "^4.0.0",
|
|
16
20
|
"@jest/globals": "^29",
|
|
17
|
-
"eslint": "^8",
|
|
21
|
+
"eslint": "^8.57.1",
|
|
22
|
+
"eslint-config-standard": "^17.1.0",
|
|
23
|
+
"eslint-plugin-import": "^2.31.0",
|
|
24
|
+
"eslint-plugin-jest": "^27.9.0",
|
|
25
|
+
"eslint-plugin-jsdoc": "^48.11.0",
|
|
26
|
+
"eslint-plugin-n": "^15.7.0",
|
|
27
|
+
"eslint-plugin-node": "^11.1.0",
|
|
28
|
+
"eslint-plugin-promise": "^6.6.0",
|
|
18
29
|
"execa": "^8",
|
|
19
30
|
"jest": "^29",
|
|
20
31
|
"memfs": "^4.11.1",
|
|
@@ -39,6 +50,18 @@
|
|
|
39
50
|
"topics": {
|
|
40
51
|
"app:state": {
|
|
41
52
|
"description": "Manage your App Builder State storage"
|
|
53
|
+
},
|
|
54
|
+
"app:db": {
|
|
55
|
+
"description": "Manage your App Builder Database storage"
|
|
56
|
+
},
|
|
57
|
+
"app:db:collection": {
|
|
58
|
+
"description": "Manage database collections, also available under 'aio app db col <command>'"
|
|
59
|
+
},
|
|
60
|
+
"app:db:document": {
|
|
61
|
+
"description": "Manage documents in a collection, also available under 'aio app db doc <command>'"
|
|
62
|
+
},
|
|
63
|
+
"app:db:index": {
|
|
64
|
+
"description": "Manage indexes on a collection, also available under 'aio app db idx <command>'"
|
|
42
65
|
}
|
|
43
66
|
},
|
|
44
67
|
"bin": "aio",
|
|
@@ -64,6 +87,10 @@
|
|
|
64
87
|
"jest": {
|
|
65
88
|
"collectCoverage": true,
|
|
66
89
|
"testEnvironment": "node",
|
|
67
|
-
"transform": {}
|
|
68
|
-
|
|
69
|
-
|
|
90
|
+
"transform": {},
|
|
91
|
+
"setupFiles": [
|
|
92
|
+
"<rootDir>/test/jest.env.js"
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
"prereleaseSha": "9c4079accc13a54a5b5c0229f13c35b5573123ab"
|
|
96
|
+
}
|
package/src/BaseCommand.js
CHANGED
|
@@ -10,24 +10,19 @@ OF ANY KIND, either express or implied. See the License for the specific languag
|
|
|
10
10
|
governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { Command
|
|
14
|
-
import config from '@adobe/aio-lib-core-config'
|
|
13
|
+
import { Command } from '@oclif/core'
|
|
15
14
|
import AioLogger from '@adobe/aio-lib-core-logging'
|
|
16
|
-
|
|
17
|
-
import { CONFIG_STATE_REGION } from './constants.js'
|
|
18
15
|
import chalk from 'chalk'
|
|
19
|
-
import semver from 'semver'
|
|
20
16
|
|
|
21
17
|
export class BaseCommand extends Command {
|
|
22
18
|
async init () {
|
|
23
19
|
await super.init()
|
|
24
|
-
// eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
25
|
-
const { readFile } = await import('fs/promises') // dynamic import to be able to mock fs, ESM and Jest are not friends
|
|
26
20
|
|
|
27
21
|
// setup debug logger
|
|
28
22
|
const command = this.constructor.name.toLowerCase() // hacky but convenient
|
|
23
|
+
const serviceName = this.getServiceName() // Get service name dynamically
|
|
29
24
|
this.debugLogger = AioLogger(
|
|
30
|
-
`aio:app
|
|
25
|
+
`aio:app:${serviceName}:${command}`,
|
|
31
26
|
{ provider: 'debug' }
|
|
32
27
|
)
|
|
33
28
|
// override warn to stderr
|
|
@@ -38,50 +33,14 @@ export class BaseCommand extends Command {
|
|
|
38
33
|
this.flags = flags
|
|
39
34
|
this.args = args
|
|
40
35
|
this.debugLogger.debug(`${command} args=${JSON.stringify(this.args)} flags=${JSON.stringify(this.flags)}`)
|
|
36
|
+
}
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.debugLogger.debug('package.json not found, skipping dependency check')
|
|
49
|
-
}
|
|
50
|
-
if (packageJson) {
|
|
51
|
-
const aioLibStateVersion = packageJson.dependencies?.['@adobe/aio-lib-state']
|
|
52
|
-
const aioSdkVersion = packageJson.dependencies?.['@adobe/aio-sdk']
|
|
53
|
-
if ((aioLibStateVersion && semver.lt(semver.coerce(aioLibStateVersion), '4.0.0')) ||
|
|
54
|
-
(aioSdkVersion && semver.lt(semver.coerce(aioSdkVersion), '6.0.0'))) {
|
|
55
|
-
this.error('State commands are not available for legacy State, please migrate to the latest "@adobe/aio-lib-state" (or "@adobe/aio-sdk" >= 6.0.0).')
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// init state client
|
|
60
|
-
const owOptions = {
|
|
61
|
-
namespace: config.get('runtime.namespace'),
|
|
62
|
-
auth: config.get('runtime.auth')
|
|
63
|
-
}
|
|
64
|
-
if (!(owOptions.namespace && owOptions.auth)) {
|
|
65
|
-
this.error(
|
|
66
|
-
`This command is expected to be run in the root of a App Builder app project.
|
|
67
|
-
Please make sure the 'AIO_RUNTIME_NAMESPACE' and 'AIO_RUNTIME_AUTH' environment variables are configured.`
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
const region = flags.region || config.get(CONFIG_STATE_REGION) || 'amer'
|
|
71
|
-
this.debugLogger.info('using state region: %s', region)
|
|
72
|
-
|
|
73
|
-
if (config.get('state.endpoint')) {
|
|
74
|
-
process.env.AIO_STATE_ENDPOINT = config.get('state.endpoint')
|
|
75
|
-
this.debugLogger.info('using custom endpoint: %s', process.env.AIO_STATE_ENDPOINT)
|
|
76
|
-
}
|
|
77
|
-
// dynamic import to be able to reload the AIO_STATE_ENDPOINT var
|
|
78
|
-
// eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
79
|
-
const State = await import('@adobe/aio-lib-state')
|
|
80
|
-
|
|
81
|
-
/** @type {import('@adobe/aio-lib-state').AdobeState} */
|
|
82
|
-
this.state = await State.init({ region, ow: owOptions })
|
|
83
|
-
|
|
84
|
-
this.rtNamespace = owOptions.namespace
|
|
38
|
+
/**
|
|
39
|
+
* Get the service name for logging
|
|
40
|
+
* @returns {string} The service name
|
|
41
|
+
*/
|
|
42
|
+
getServiceName () {
|
|
43
|
+
return 'app' // Default fallback
|
|
85
44
|
}
|
|
86
45
|
|
|
87
46
|
async catch (error) {
|
|
@@ -105,12 +64,6 @@ export class BaseCommand extends Command {
|
|
|
105
64
|
// display the JSON returned by the command's run method.
|
|
106
65
|
BaseCommand.enableJsonFlag = true
|
|
107
66
|
|
|
108
|
-
BaseCommand.flags = {
|
|
109
|
-
region: Flags.string({
|
|
110
|
-
description: 'State region. Defaults to \'AIO_STATE_REGION\' env or \'amer\' if neither is set.',
|
|
111
|
-
required: false,
|
|
112
|
-
options: ['amer', 'emea']
|
|
113
|
-
})
|
|
114
|
-
}
|
|
67
|
+
BaseCommand.flags = {}
|
|
115
68
|
|
|
116
69
|
BaseCommand.args = {}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 Adobe. All rights reserved.
|
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { BaseCommand } from './BaseCommand.js'
|
|
14
|
+
import config from '@adobe/aio-lib-core-config'
|
|
15
|
+
import { CONFIG_RUNTIME_AUTH, CONFIG_RUNTIME_NAMESPACE } from './constants/global.js'
|
|
16
|
+
import { AVAILABLE_REGIONS, CONFIG_DB_ENDPOINT, CONFIG_DB_REGION, DEFAULT_REGION } from './constants/db.js'
|
|
17
|
+
import { Flags } from '@oclif/core'
|
|
18
|
+
import { getCliEnv } from '@adobe/aio-lib-env'
|
|
19
|
+
|
|
20
|
+
export class DBBaseCommand extends BaseCommand {
|
|
21
|
+
async init () {
|
|
22
|
+
await super.init()
|
|
23
|
+
|
|
24
|
+
// Initialize database client
|
|
25
|
+
await this.initializeDBClient()
|
|
26
|
+
|
|
27
|
+
this.debugLogger?.info?.('DBBaseCommand initialized with DB client')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Initialize the database client using aio-lib-db
|
|
32
|
+
*/
|
|
33
|
+
async initializeDBClient () {
|
|
34
|
+
try {
|
|
35
|
+
const region = this.flags?.region || config.get(CONFIG_DB_REGION) || DEFAULT_REGION
|
|
36
|
+
// Get database configuration
|
|
37
|
+
const dbConfig = {
|
|
38
|
+
ow: {
|
|
39
|
+
namespace: config.get(CONFIG_RUNTIME_NAMESPACE),
|
|
40
|
+
auth: config.get(CONFIG_RUNTIME_AUTH)
|
|
41
|
+
},
|
|
42
|
+
region
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Validate region based on environment
|
|
46
|
+
const allowedRegions = AVAILABLE_REGIONS[getCliEnv()]
|
|
47
|
+
if (!allowedRegions.includes(region)) {
|
|
48
|
+
this.error(`Invalid region '${region}' for the ${getCliEnv()} environment, must be one of: ${allowedRegions.join(', ')}`)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Validate required configuration
|
|
52
|
+
if (!(dbConfig.ow.namespace && dbConfig.ow.auth)) {
|
|
53
|
+
this.error(
|
|
54
|
+
`Database commands require App Builder project configuration.
|
|
55
|
+
Please make sure the 'AIO_RUNTIME_NAMESPACE' and 'AIO_RUNTIME_AUTH' environment variables are configured.`
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const endpointOverride = config.get(CONFIG_DB_ENDPOINT)
|
|
60
|
+
if (endpointOverride) {
|
|
61
|
+
process.env.AIO_DB_ENDPOINT = endpointOverride
|
|
62
|
+
this.debugLogger?.info?.('Using custom endpoint: %s', process.env.AIO_DB_ENDPOINT)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Dynamic import to be able to reload the AIO_DB_ENDPOINT var
|
|
66
|
+
// eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
67
|
+
const aioLibDb = await import('@adobe/aio-lib-db')
|
|
68
|
+
const { init } = aioLibDb.default || aioLibDb
|
|
69
|
+
|
|
70
|
+
this.debugLogger?.info?.('Initializing DB client with config:', {
|
|
71
|
+
namespace: dbConfig.ow.namespace,
|
|
72
|
+
region: dbConfig.region,
|
|
73
|
+
hasAuth: !!dbConfig.ow.auth
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// Initialize the database client
|
|
77
|
+
this.db = await init(dbConfig)
|
|
78
|
+
this.dbConfig = dbConfig
|
|
79
|
+
this.rtNamespace = dbConfig.ow.namespace
|
|
80
|
+
|
|
81
|
+
this.debugLogger?.info?.('DB client initialized successfully')
|
|
82
|
+
} catch (error) {
|
|
83
|
+
this.debugLogger?.error?.('Failed to initialize DB client:', error.message)
|
|
84
|
+
this.error(`Failed to initialize database client: ${error.message}`)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get the service name for logging
|
|
90
|
+
* @returns {string} The service name
|
|
91
|
+
*/
|
|
92
|
+
getServiceName () {
|
|
93
|
+
return 'db'
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Add json and region flags to GLOBAL FLAGS section in --help output
|
|
98
|
+
DBBaseCommand.flags = {
|
|
99
|
+
...BaseCommand.flags,
|
|
100
|
+
json: {
|
|
101
|
+
description: 'Format output as json.',
|
|
102
|
+
default: false,
|
|
103
|
+
required: false,
|
|
104
|
+
helpGroup: 'GLOBAL'
|
|
105
|
+
},
|
|
106
|
+
region: Flags.string({
|
|
107
|
+
description: `Database region. Defaults to 'AIO_DB_REGION' environment variable or '${DEFAULT_REGION}' if neither is set.\n<options: ${AVAILABLE_REGIONS.prod.join('|')}>`,
|
|
108
|
+
required: false,
|
|
109
|
+
helpGroup: 'GLOBAL'
|
|
110
|
+
// Don't set default here to let it load from the environment var if not passed as a flag
|
|
111
|
+
})
|
|
112
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2024 Adobe. All rights reserved.
|
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { BaseCommand } from './BaseCommand.js'
|
|
14
|
+
import config from '@adobe/aio-lib-core-config'
|
|
15
|
+
import { AVAILABLE_REGIONS, CONFIG_STATE_REGION, DEFAULT_REGION } from './constants/state.js'
|
|
16
|
+
import { CONFIG_RUNTIME_NAMESPACE, CONFIG_RUNTIME_AUTH } from './constants/global.js'
|
|
17
|
+
|
|
18
|
+
import semver from 'semver'
|
|
19
|
+
import { Flags } from '@oclif/core'
|
|
20
|
+
|
|
21
|
+
export class StateBaseCommand extends BaseCommand {
|
|
22
|
+
async init () {
|
|
23
|
+
await super.init()
|
|
24
|
+
// check application dependencies
|
|
25
|
+
let packageJson
|
|
26
|
+
try {
|
|
27
|
+
// eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
28
|
+
const { readFile } = await import('fs/promises')
|
|
29
|
+
const file = await readFile('package.json')
|
|
30
|
+
packageJson = JSON.parse(file.toString())
|
|
31
|
+
} catch (e) {
|
|
32
|
+
this.debugLogger?.debug?.('package.json not found, skipping dependency check')
|
|
33
|
+
}
|
|
34
|
+
if (packageJson) {
|
|
35
|
+
const aioLibStateVersion = packageJson.dependencies?.['@adobe/aio-lib-state']
|
|
36
|
+
const aioSdkVersion = packageJson.dependencies?.['@adobe/aio-sdk']
|
|
37
|
+
if ((aioLibStateVersion && semver.lt(semver.coerce(aioLibStateVersion), '4.0.0')) ||
|
|
38
|
+
(aioSdkVersion && semver.lt(semver.coerce(aioSdkVersion), '6.0.0'))) {
|
|
39
|
+
this.error('State commands are not available for legacy State, please migrate to the latest "@adobe/aio-lib-state" (or "@adobe/aio-sdk" >= 6.0.0).')
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// init state client
|
|
44
|
+
const owOptions = {
|
|
45
|
+
namespace: config.get(CONFIG_RUNTIME_NAMESPACE),
|
|
46
|
+
auth: config.get(CONFIG_RUNTIME_AUTH)
|
|
47
|
+
}
|
|
48
|
+
if (!(owOptions.namespace && owOptions.auth)) {
|
|
49
|
+
this.error(
|
|
50
|
+
`This command is expected to be run in the root of a App Builder app project.
|
|
51
|
+
Please make sure the 'AIO_RUNTIME_NAMESPACE' and 'AIO_RUNTIME_AUTH' environment variables are configured.`
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
const region = this.flags.region || config.get(CONFIG_STATE_REGION) || DEFAULT_REGION
|
|
55
|
+
this.debugLogger?.info?.('using state region: %s', region)
|
|
56
|
+
|
|
57
|
+
if (config.get('state.endpoint')) {
|
|
58
|
+
process.env.AIO_STATE_ENDPOINT = config.get('state.endpoint')
|
|
59
|
+
this.debugLogger?.info?.('using custom endpoint: %s', process.env.AIO_STATE_ENDPOINT)
|
|
60
|
+
}
|
|
61
|
+
// dynamic import to be able to reload the AIO_STATE_ENDPOINT var
|
|
62
|
+
// eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
63
|
+
const aioLibState = await import('@adobe/aio-lib-state')
|
|
64
|
+
|
|
65
|
+
/** @type {import('@adobe/aio-lib-state').AdobeState} */
|
|
66
|
+
this.state = await aioLibState.init({ region, ow: owOptions })
|
|
67
|
+
|
|
68
|
+
this.rtNamespace = owOptions.namespace
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get the service name for logging
|
|
73
|
+
* @returns {string} The service name
|
|
74
|
+
*/
|
|
75
|
+
getServiceName () {
|
|
76
|
+
return 'state'
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
StateBaseCommand.flags = {
|
|
81
|
+
...BaseCommand.flags,
|
|
82
|
+
region: Flags.string({
|
|
83
|
+
description: 'State region. Defaults to \'AIO_STATE_REGION\' env or \'amer\' if neither is set.',
|
|
84
|
+
required: false,
|
|
85
|
+
options: AVAILABLE_REGIONS
|
|
86
|
+
})
|
|
87
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 Adobe. All rights reserved.
|
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { Provision } from '../db/provision.js'
|
|
14
|
+
|
|
15
|
+
// 'aio app add db' is an alias for 'aio app db provision', but to have it show up in
|
|
16
|
+
// help correctly it needs its own class instead of using Provision.aliases
|
|
17
|
+
export class AddDb extends Provision {}
|
|
18
|
+
|
|
19
|
+
// Update the examples for the help display
|
|
20
|
+
AddDb.examples = Provision.examples.map(example => example.replace('$ aio app db provision', '$ aio app add db'))
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 Adobe. All rights reserved.
|
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { DBBaseCommand } from '../../../../DBBaseCommand.js'
|
|
14
|
+
import { Args, Flags } from '@oclif/core'
|
|
15
|
+
import chalk from 'chalk'
|
|
16
|
+
import { asObject, isNonEmptyString } from '../../../../utils/inputValidation.js'
|
|
17
|
+
|
|
18
|
+
export class CreateCollection extends DBBaseCommand {
|
|
19
|
+
async run () {
|
|
20
|
+
const { collection } = this.args
|
|
21
|
+
const { validator } = this.flags
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
this.log(chalk.blue(`Creating collection '${collection}'...`))
|
|
25
|
+
|
|
26
|
+
// Log flag values if set
|
|
27
|
+
if (validator) {
|
|
28
|
+
this.log(chalk.dim(` Using validator: ${validator}`))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const client = await this.db.connect()
|
|
32
|
+
|
|
33
|
+
// Check if collection already exists
|
|
34
|
+
const existingCollections = await client.listCollections()
|
|
35
|
+
const collectionExists = existingCollections && existingCollections.some(col => col.name === collection)
|
|
36
|
+
|
|
37
|
+
if (collectionExists) {
|
|
38
|
+
const errorMessage = `Collection '${collection}' already exists`
|
|
39
|
+
|
|
40
|
+
this.log(chalk.red(errorMessage))
|
|
41
|
+
this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
|
|
42
|
+
|
|
43
|
+
this.error(errorMessage)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Build collection options
|
|
47
|
+
const options = {}
|
|
48
|
+
if (validator) {
|
|
49
|
+
options.validator = { $jsonSchema: validator }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Create the collection
|
|
53
|
+
const result = await client.createCollection(collection, options)
|
|
54
|
+
|
|
55
|
+
this.debugLogger?.info?.('Collection created successfully:', result)
|
|
56
|
+
|
|
57
|
+
const response = {
|
|
58
|
+
collection,
|
|
59
|
+
status: 'created',
|
|
60
|
+
namespace: this.rtNamespace,
|
|
61
|
+
timestamp: new Date().toISOString(),
|
|
62
|
+
options
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.log(chalk.green(`Collection '${collection}' created successfully`))
|
|
66
|
+
this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
|
|
67
|
+
|
|
68
|
+
if (validator) {
|
|
69
|
+
// Display the final validator object (after parsing) in compact JSON format
|
|
70
|
+
const validatorDisplay = JSON.stringify(options.validator.$jsonSchema)
|
|
71
|
+
this.log(chalk.dim(` Validator: ${validatorDisplay}`))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this.log(chalk.dim(` Created: ${new Date().toLocaleString()}`))
|
|
75
|
+
|
|
76
|
+
return response
|
|
77
|
+
} catch (error) {
|
|
78
|
+
this.debugLogger?.error?.('Error creating collection:', error)
|
|
79
|
+
|
|
80
|
+
const errorMessage = `Failed to create collection '${collection}': ${error.message}`
|
|
81
|
+
|
|
82
|
+
this.log(chalk.red('Failed to create collection'))
|
|
83
|
+
this.log(chalk.dim(` Collection: ${collection}`))
|
|
84
|
+
this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
|
|
85
|
+
this.log(chalk.dim(` Error: ${error.message}`))
|
|
86
|
+
|
|
87
|
+
this.error(errorMessage)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
CreateCollection.description = 'Create a new collection in the database'
|
|
93
|
+
|
|
94
|
+
CreateCollection.examples = [
|
|
95
|
+
'$ aio app db collection create users',
|
|
96
|
+
'$ aio app db collection create inventory --validator \'{"type": "object", "required": ["id", "quantity"]}\' --json',
|
|
97
|
+
'$ aio app db col create products --json',
|
|
98
|
+
'$ aio app db col create products --validator \'{"type": "object", "properties": {"name": {"type": "string"}, "price": {"type": "number", "minimum": 0}}, "required": ["name", "price"]}\''
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
CreateCollection.args = {
|
|
102
|
+
collection: Args.string({
|
|
103
|
+
name: 'collection',
|
|
104
|
+
description: 'The name of the collection to create',
|
|
105
|
+
required: true,
|
|
106
|
+
parse: input => isNonEmptyString(input, 'Collection name')
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
CreateCollection.flags = {
|
|
111
|
+
...DBBaseCommand.flags,
|
|
112
|
+
validator: Flags.string({
|
|
113
|
+
char: 'v',
|
|
114
|
+
description: 'JSON schema validator for document validation (JSON string)',
|
|
115
|
+
parse: input => asObject(input, 'Validator')
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
CreateCollection.aliases = ['app:db:col:create']
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 Adobe. All rights reserved.
|
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { DBBaseCommand } from '../../../../DBBaseCommand.js'
|
|
14
|
+
import { Args } from '@oclif/core'
|
|
15
|
+
import chalk from 'chalk'
|
|
16
|
+
import { isNonEmptyString } from '../../../../utils/inputValidation.js'
|
|
17
|
+
import { prettyJson } from '../../../../utils/output.js'
|
|
18
|
+
|
|
19
|
+
export class DropCollection extends DBBaseCommand {
|
|
20
|
+
async run () {
|
|
21
|
+
const { collection } = this.args
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
this.log(chalk.blue(`Dropping collection '${collection}'...`))
|
|
25
|
+
|
|
26
|
+
const client = await this.db.connect()
|
|
27
|
+
|
|
28
|
+
// Get the collection object
|
|
29
|
+
const coll = client.collection(collection)
|
|
30
|
+
|
|
31
|
+
// Drop collection
|
|
32
|
+
const result = await coll.drop()
|
|
33
|
+
|
|
34
|
+
this.debugLogger?.info?.('Collection dropped successfully:', result)
|
|
35
|
+
|
|
36
|
+
const response = {
|
|
37
|
+
collection,
|
|
38
|
+
status: 'dropped',
|
|
39
|
+
namespace: this.rtNamespace,
|
|
40
|
+
timestamp: new Date().toISOString(),
|
|
41
|
+
result
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.log(chalk.green(`Collection '${collection}' dropped successfully`))
|
|
45
|
+
this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
|
|
46
|
+
|
|
47
|
+
if (result && typeof result === 'object' && Object.keys(result).length > 0) {
|
|
48
|
+
this.log(chalk.dim(` Details:\n${prettyJson(result)}`))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.log(chalk.dim(` Dropped: ${new Date().toLocaleString()}`))
|
|
52
|
+
|
|
53
|
+
return response
|
|
54
|
+
} catch (error) {
|
|
55
|
+
this.debugLogger?.error?.('Error dropping collection:', error)
|
|
56
|
+
|
|
57
|
+
const errorMessage = `Failed to drop collection '${collection}': ${error.message}`
|
|
58
|
+
|
|
59
|
+
this.log(chalk.red('Failed to drop collection'))
|
|
60
|
+
this.log(chalk.dim(` Collection: ${collection}`))
|
|
61
|
+
this.log(chalk.dim(` Namespace: ${this.rtNamespace}`))
|
|
62
|
+
this.log(chalk.dim(` Error: ${error.message}`))
|
|
63
|
+
|
|
64
|
+
this.error(errorMessage)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
DropCollection.description = 'Drop a collection from the database'
|
|
70
|
+
|
|
71
|
+
DropCollection.examples = [
|
|
72
|
+
'$ aio app db collection drop users',
|
|
73
|
+
'$ aio app db collection drop products --json',
|
|
74
|
+
'$ aio app db col drop inventory'
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
DropCollection.args = {
|
|
78
|
+
collection: Args.string({
|
|
79
|
+
name: 'collection',
|
|
80
|
+
description: 'The name of the collection to drop',
|
|
81
|
+
required: true,
|
|
82
|
+
parse: input => isNonEmptyString(input, 'Collection name')
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
DropCollection.flags = {
|
|
87
|
+
...DBBaseCommand.flags
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
DropCollection.aliases = ['app:db:col:drop']
|