@xano/cli 0.0.33 → 0.0.34
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/dist/commands/workflow_test/delete/index.d.ts +21 -0
- package/dist/commands/workflow_test/delete/index.js +130 -0
- package/dist/commands/workflow_test/get/index.d.ts +20 -0
- package/dist/commands/workflow_test/get/index.js +142 -0
- package/dist/commands/workflow_test/list/index.d.ts +14 -0
- package/dist/commands/workflow_test/list/index.js +129 -0
- package/dist/commands/workflow_test/run/index.d.ts +19 -0
- package/dist/commands/workflow_test/run/index.js +119 -0
- package/dist/commands/workflow_test/run_all/index.d.ts +14 -0
- package/dist/commands/workflow_test/run_all/index.js +204 -0
- package/oclif.manifest.json +1476 -1077
- package/package.json +4 -1
- /package/dist/commands/branch/{set-live → set_live}/index.d.ts +0 -0
- /package/dist/commands/branch/{set-live → set_live}/index.js +0 -0
- /package/dist/commands/profile/{get-default → get_default}/index.d.ts +0 -0
- /package/dist/commands/profile/{get-default → get_default}/index.js +0 -0
- /package/dist/commands/profile/{set-default → set_default}/index.d.ts +0 -0
- /package/dist/commands/profile/{set-default → set_default}/index.js +0 -0
- /package/dist/commands/tenant/{deploy-platform → deploy_platform}/index.d.ts +0 -0
- /package/dist/commands/tenant/{deploy-platform → deploy_platform}/index.js +0 -0
- /package/dist/commands/tenant/{deploy-release → deploy_release}/index.d.ts +0 -0
- /package/dist/commands/tenant/{deploy-release → deploy_release}/index.js +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class WorkflowTestDelete extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
workflow_test_id: import("@oclif/core/interfaces").Arg<number, {
|
|
5
|
+
max?: number;
|
|
6
|
+
min?: number;
|
|
7
|
+
}>;
|
|
8
|
+
};
|
|
9
|
+
static description: string;
|
|
10
|
+
static examples: string[];
|
|
11
|
+
static flags: {
|
|
12
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
private confirm;
|
|
20
|
+
private loadCredentials;
|
|
21
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import * as readline from 'node:readline';
|
|
3
|
+
import * as yaml from 'js-yaml';
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import * as os from 'node:os';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import BaseCommand from '../../../base-command.js';
|
|
8
|
+
export default class WorkflowTestDelete extends BaseCommand {
|
|
9
|
+
static args = {
|
|
10
|
+
workflow_test_id: Args.integer({
|
|
11
|
+
description: 'ID of the workflow test to delete',
|
|
12
|
+
required: true,
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
static description = 'Delete a workflow test';
|
|
16
|
+
static examples = [
|
|
17
|
+
`$ xano workflow-test delete 1
|
|
18
|
+
Are you sure you want to delete workflow test 1? (y/N) y
|
|
19
|
+
Deleted workflow test 1
|
|
20
|
+
`,
|
|
21
|
+
`$ xano workflow-test delete 1 --force`,
|
|
22
|
+
];
|
|
23
|
+
static flags = {
|
|
24
|
+
...BaseCommand.baseFlags,
|
|
25
|
+
force: Flags.boolean({
|
|
26
|
+
char: 'f',
|
|
27
|
+
default: false,
|
|
28
|
+
description: 'Skip confirmation prompt',
|
|
29
|
+
required: false,
|
|
30
|
+
}),
|
|
31
|
+
output: Flags.string({
|
|
32
|
+
char: 'o',
|
|
33
|
+
default: 'summary',
|
|
34
|
+
description: 'Output format',
|
|
35
|
+
options: ['summary', 'json'],
|
|
36
|
+
required: false,
|
|
37
|
+
}),
|
|
38
|
+
workspace: Flags.string({
|
|
39
|
+
char: 'w',
|
|
40
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
41
|
+
required: false,
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
async run() {
|
|
45
|
+
const { args, flags } = await this.parse(WorkflowTestDelete);
|
|
46
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
47
|
+
const credentials = this.loadCredentials();
|
|
48
|
+
if (!(profileName in credentials.profiles)) {
|
|
49
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
50
|
+
`Create a profile using 'xano profile create'`);
|
|
51
|
+
}
|
|
52
|
+
const profile = credentials.profiles[profileName];
|
|
53
|
+
if (!profile.instance_origin) {
|
|
54
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
55
|
+
}
|
|
56
|
+
if (!profile.access_token) {
|
|
57
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
58
|
+
}
|
|
59
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
60
|
+
if (!workspaceId) {
|
|
61
|
+
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
62
|
+
}
|
|
63
|
+
if (!flags.force) {
|
|
64
|
+
const confirmed = await this.confirm(`Are you sure you want to delete workflow test ${args.workflow_test_id}? (y/N) `);
|
|
65
|
+
if (!confirmed) {
|
|
66
|
+
this.log('Deletion cancelled.');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/workflow_test/${args.workflow_test_id}`;
|
|
71
|
+
try {
|
|
72
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
73
|
+
headers: {
|
|
74
|
+
'accept': 'application/json',
|
|
75
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
76
|
+
},
|
|
77
|
+
method: 'DELETE',
|
|
78
|
+
}, flags.verbose, profile.access_token);
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
const errorText = await response.text();
|
|
81
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
82
|
+
}
|
|
83
|
+
if (flags.output === 'json') {
|
|
84
|
+
this.log(JSON.stringify({ deleted: true, workflow_test_id: args.workflow_test_id }, null, 2));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
this.log(`Deleted workflow test ${args.workflow_test_id}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (error instanceof Error) {
|
|
92
|
+
this.error(`Failed to delete workflow test: ${error.message}`);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
this.error(`Failed to delete workflow test: ${String(error)}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async confirm(message) {
|
|
100
|
+
const rl = readline.createInterface({
|
|
101
|
+
input: process.stdin,
|
|
102
|
+
output: process.stdout,
|
|
103
|
+
});
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
rl.question(message, (answer) => {
|
|
106
|
+
rl.close();
|
|
107
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
loadCredentials() {
|
|
112
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
113
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
114
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
115
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
116
|
+
`Create a profile using 'xano profile create'`);
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
120
|
+
const parsed = yaml.load(fileContent);
|
|
121
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
122
|
+
this.error('Credentials file has invalid format.');
|
|
123
|
+
}
|
|
124
|
+
return parsed;
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class WorkflowTestGet extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
workflow_test_id: import("@oclif/core/interfaces").Arg<number, {
|
|
5
|
+
max?: number;
|
|
6
|
+
min?: number;
|
|
7
|
+
}>;
|
|
8
|
+
};
|
|
9
|
+
static description: string;
|
|
10
|
+
static examples: string[];
|
|
11
|
+
static flags: {
|
|
12
|
+
'include-draft': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
private loadCredentials;
|
|
20
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import * as yaml from 'js-yaml';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
export default class WorkflowTestGet extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
workflow_test_id: Args.integer({
|
|
10
|
+
description: 'ID of the workflow test',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static description = 'Get a specific workflow test';
|
|
15
|
+
static examples = [
|
|
16
|
+
`$ xano workflow-test get 1
|
|
17
|
+
Workflow Test: my-test (ID: 1)
|
|
18
|
+
Description: Validates auth endpoints
|
|
19
|
+
Branch: main
|
|
20
|
+
`,
|
|
21
|
+
`$ xano workflow-test get 1 -o xs`,
|
|
22
|
+
`$ xano workflow-test get 1 -o json`,
|
|
23
|
+
];
|
|
24
|
+
static flags = {
|
|
25
|
+
...BaseCommand.baseFlags,
|
|
26
|
+
'include-draft': Flags.boolean({
|
|
27
|
+
default: false,
|
|
28
|
+
description: 'Include draft version',
|
|
29
|
+
required: false,
|
|
30
|
+
}),
|
|
31
|
+
output: Flags.string({
|
|
32
|
+
char: 'o',
|
|
33
|
+
default: 'summary',
|
|
34
|
+
description: 'Output format',
|
|
35
|
+
options: ['summary', 'json', 'xs'],
|
|
36
|
+
required: false,
|
|
37
|
+
}),
|
|
38
|
+
workspace: Flags.string({
|
|
39
|
+
char: 'w',
|
|
40
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
41
|
+
required: false,
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
async run() {
|
|
45
|
+
const { args, flags } = await this.parse(WorkflowTestGet);
|
|
46
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
47
|
+
const credentials = this.loadCredentials();
|
|
48
|
+
if (!(profileName in credentials.profiles)) {
|
|
49
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
50
|
+
`Create a profile using 'xano profile create'`);
|
|
51
|
+
}
|
|
52
|
+
const profile = credentials.profiles[profileName];
|
|
53
|
+
if (!profile.instance_origin) {
|
|
54
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
55
|
+
}
|
|
56
|
+
if (!profile.access_token) {
|
|
57
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
58
|
+
}
|
|
59
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
60
|
+
if (!workspaceId) {
|
|
61
|
+
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
62
|
+
}
|
|
63
|
+
const params = new URLSearchParams({ include_xanoscript: 'true' });
|
|
64
|
+
if (flags['include-draft']) {
|
|
65
|
+
params.set('include_draft', 'true');
|
|
66
|
+
}
|
|
67
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/workflow_test/${args.workflow_test_id}?${params}`;
|
|
68
|
+
try {
|
|
69
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
70
|
+
headers: {
|
|
71
|
+
'accept': 'application/json',
|
|
72
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
73
|
+
},
|
|
74
|
+
method: 'GET',
|
|
75
|
+
}, flags.verbose, profile.access_token);
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
const errorText = await response.text();
|
|
78
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
79
|
+
}
|
|
80
|
+
const test = await response.json();
|
|
81
|
+
if (flags.output === 'json') {
|
|
82
|
+
this.log(JSON.stringify(test, null, 2));
|
|
83
|
+
}
|
|
84
|
+
else if (flags.output === 'xs') {
|
|
85
|
+
if (test.xanoscript?.status === 'ok' && test.xanoscript.value) {
|
|
86
|
+
this.log(test.xanoscript.value);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
this.error(`XanoScript not available: ${test.xanoscript?.message || 'unknown error'}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
this.log(`Workflow Test: ${test.name} (ID: ${test.id})`);
|
|
94
|
+
if (test.description) {
|
|
95
|
+
this.log(` Description: ${test.description}`);
|
|
96
|
+
}
|
|
97
|
+
if (test.branch?.name) {
|
|
98
|
+
this.log(` Branch: ${test.branch.name}`);
|
|
99
|
+
}
|
|
100
|
+
else if (test.branch?.id) {
|
|
101
|
+
this.log(` Branch ID: ${test.branch.id}`);
|
|
102
|
+
}
|
|
103
|
+
if (test.tag && test.tag.length > 0) {
|
|
104
|
+
this.log(` Tags: ${test.tag.join(', ')}`);
|
|
105
|
+
}
|
|
106
|
+
if (test.created_at) {
|
|
107
|
+
this.log(` Created: ${test.created_at}`);
|
|
108
|
+
}
|
|
109
|
+
if (test.updated_at) {
|
|
110
|
+
this.log(` Updated: ${test.updated_at}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (error instanceof Error) {
|
|
116
|
+
this.error(`Failed to get workflow test: ${error.message}`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
this.error(`Failed to get workflow test: ${String(error)}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
loadCredentials() {
|
|
124
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
125
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
126
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
127
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
128
|
+
`Create a profile using 'xano profile create'`);
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
132
|
+
const parsed = yaml.load(fileContent);
|
|
133
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
134
|
+
this.error('Credentials file has invalid format.');
|
|
135
|
+
}
|
|
136
|
+
return parsed;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class WorkflowTestList extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
private loadCredentials;
|
|
14
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import * as yaml from 'js-yaml';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
export default class WorkflowTestList extends BaseCommand {
|
|
8
|
+
static description = 'List all workflow tests in a workspace';
|
|
9
|
+
static examples = [
|
|
10
|
+
`$ xano workflow-test list
|
|
11
|
+
Workflow tests in workspace 5:
|
|
12
|
+
- my-test (ID: 1)
|
|
13
|
+
- auth-flow (ID: 2) - Validates auth endpoints
|
|
14
|
+
`,
|
|
15
|
+
`$ xano workflow-test list -w 5 --output json`,
|
|
16
|
+
`$ xano workflow-test list --branch main`,
|
|
17
|
+
];
|
|
18
|
+
static flags = {
|
|
19
|
+
...BaseCommand.baseFlags,
|
|
20
|
+
branch: Flags.string({
|
|
21
|
+
char: 'b',
|
|
22
|
+
description: 'Filter by branch name',
|
|
23
|
+
required: false,
|
|
24
|
+
}),
|
|
25
|
+
output: Flags.string({
|
|
26
|
+
char: 'o',
|
|
27
|
+
default: 'summary',
|
|
28
|
+
description: 'Output format',
|
|
29
|
+
options: ['summary', 'json'],
|
|
30
|
+
required: false,
|
|
31
|
+
}),
|
|
32
|
+
workspace: Flags.string({
|
|
33
|
+
char: 'w',
|
|
34
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
35
|
+
required: false,
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
async run() {
|
|
39
|
+
const { flags } = await this.parse(WorkflowTestList);
|
|
40
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
41
|
+
const credentials = this.loadCredentials();
|
|
42
|
+
if (!(profileName in credentials.profiles)) {
|
|
43
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
44
|
+
`Create a profile using 'xano profile create'`);
|
|
45
|
+
}
|
|
46
|
+
const profile = credentials.profiles[profileName];
|
|
47
|
+
if (!profile.instance_origin) {
|
|
48
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
49
|
+
}
|
|
50
|
+
if (!profile.access_token) {
|
|
51
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
52
|
+
}
|
|
53
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
54
|
+
if (!workspaceId) {
|
|
55
|
+
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
56
|
+
}
|
|
57
|
+
const params = new URLSearchParams({ include_xanoscript: 'false' });
|
|
58
|
+
if (flags.branch) {
|
|
59
|
+
params.set('branch', flags.branch);
|
|
60
|
+
}
|
|
61
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/workflow_test?${params}`;
|
|
62
|
+
try {
|
|
63
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
64
|
+
headers: {
|
|
65
|
+
'accept': 'application/json',
|
|
66
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
67
|
+
},
|
|
68
|
+
method: 'GET',
|
|
69
|
+
}, flags.verbose, profile.access_token);
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
const errorText = await response.text();
|
|
72
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
73
|
+
}
|
|
74
|
+
const data = await response.json();
|
|
75
|
+
let tests;
|
|
76
|
+
if (Array.isArray(data)) {
|
|
77
|
+
tests = data;
|
|
78
|
+
}
|
|
79
|
+
else if (data && typeof data === 'object' && 'items' in data && Array.isArray(data.items)) {
|
|
80
|
+
tests = data.items;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
this.error('Unexpected API response format');
|
|
84
|
+
}
|
|
85
|
+
if (flags.output === 'json') {
|
|
86
|
+
this.log(JSON.stringify(tests, null, 2));
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
if (tests.length === 0) {
|
|
90
|
+
this.log('No workflow tests found');
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
this.log(`Workflow tests in workspace ${workspaceId}:`);
|
|
94
|
+
for (const test of tests) {
|
|
95
|
+
const desc = test.description ? ` - ${test.description}` : '';
|
|
96
|
+
this.log(` - ${test.name} (ID: ${test.id})${desc}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (error instanceof Error) {
|
|
103
|
+
this.error(`Failed to list workflow tests: ${error.message}`);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
this.error(`Failed to list workflow tests: ${String(error)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
loadCredentials() {
|
|
111
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
112
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
113
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
114
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
115
|
+
`Create a profile using 'xano profile create'`);
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
119
|
+
const parsed = yaml.load(fileContent);
|
|
120
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
121
|
+
this.error('Credentials file has invalid format.');
|
|
122
|
+
}
|
|
123
|
+
return parsed;
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class WorkflowTestRun extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
workflow_test_id: import("@oclif/core/interfaces").Arg<number, {
|
|
5
|
+
max?: number;
|
|
6
|
+
min?: number;
|
|
7
|
+
}>;
|
|
8
|
+
};
|
|
9
|
+
static description: string;
|
|
10
|
+
static examples: string[];
|
|
11
|
+
static flags: {
|
|
12
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
};
|
|
17
|
+
run(): Promise<void>;
|
|
18
|
+
private loadCredentials;
|
|
19
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import * as yaml from 'js-yaml';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
export default class WorkflowTestRun extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
workflow_test_id: Args.integer({
|
|
10
|
+
description: 'ID of the workflow test to run',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static description = 'Run a workflow test';
|
|
15
|
+
static examples = [
|
|
16
|
+
`$ xano workflow-test run 1
|
|
17
|
+
Running workflow test 1...
|
|
18
|
+
Result: PASS (1.234s)
|
|
19
|
+
`,
|
|
20
|
+
`$ xano workflow-test run 1 -o json`,
|
|
21
|
+
];
|
|
22
|
+
static flags = {
|
|
23
|
+
...BaseCommand.baseFlags,
|
|
24
|
+
output: Flags.string({
|
|
25
|
+
char: 'o',
|
|
26
|
+
default: 'summary',
|
|
27
|
+
description: 'Output format',
|
|
28
|
+
options: ['summary', 'json'],
|
|
29
|
+
required: false,
|
|
30
|
+
}),
|
|
31
|
+
workspace: Flags.string({
|
|
32
|
+
char: 'w',
|
|
33
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
34
|
+
required: false,
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
async run() {
|
|
38
|
+
const { args, flags } = await this.parse(WorkflowTestRun);
|
|
39
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
40
|
+
const credentials = this.loadCredentials();
|
|
41
|
+
if (!(profileName in credentials.profiles)) {
|
|
42
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
43
|
+
`Create a profile using 'xano profile create'`);
|
|
44
|
+
}
|
|
45
|
+
const profile = credentials.profiles[profileName];
|
|
46
|
+
if (!profile.instance_origin) {
|
|
47
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
48
|
+
}
|
|
49
|
+
if (!profile.access_token) {
|
|
50
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
51
|
+
}
|
|
52
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
53
|
+
if (!workspaceId) {
|
|
54
|
+
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
55
|
+
}
|
|
56
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/workflow_test/${args.workflow_test_id}/run`;
|
|
57
|
+
try {
|
|
58
|
+
if (flags.output === 'summary') {
|
|
59
|
+
this.log(`Running workflow test ${args.workflow_test_id}...`);
|
|
60
|
+
}
|
|
61
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
62
|
+
headers: {
|
|
63
|
+
'accept': 'application/json',
|
|
64
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
65
|
+
'Content-Type': 'application/json',
|
|
66
|
+
},
|
|
67
|
+
method: 'POST',
|
|
68
|
+
}, flags.verbose, profile.access_token);
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
const errorText = await response.text();
|
|
71
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
72
|
+
}
|
|
73
|
+
const result = await response.json();
|
|
74
|
+
if (flags.output === 'json') {
|
|
75
|
+
this.log(JSON.stringify(result, null, 2));
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
const timing = `(${result.timing.toFixed(3)}s)`;
|
|
79
|
+
if (result.status === 'ok') {
|
|
80
|
+
this.log(`Result: PASS ${timing}`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
this.log(`Result: FAIL ${timing}`);
|
|
84
|
+
if (result.message) {
|
|
85
|
+
this.log(` Error: ${result.message}`);
|
|
86
|
+
}
|
|
87
|
+
this.exit(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (error instanceof Error) {
|
|
93
|
+
this.error(`Failed to run workflow test: ${error.message}`);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.error(`Failed to run workflow test: ${String(error)}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
loadCredentials() {
|
|
101
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
102
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
103
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
104
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
105
|
+
`Create a profile using 'xano profile create'`);
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
109
|
+
const parsed = yaml.load(fileContent);
|
|
110
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
111
|
+
this.error('Credentials file has invalid format.');
|
|
112
|
+
}
|
|
113
|
+
return parsed;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class WorkflowTestRunAll extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
private loadCredentials;
|
|
14
|
+
}
|