@apollo-annotation/cli 0.1.10
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 +899 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +9 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +8 -0
- package/dist/Config.d.ts +43 -0
- package/dist/Config.js +233 -0
- package/dist/baseCommand.d.ts +21 -0
- package/dist/baseCommand.js +62 -0
- package/dist/commands/assembly/add-fasta.d.ts +15 -0
- package/dist/commands/assembly/add-fasta.js +98 -0
- package/dist/commands/assembly/add-gff.d.ts +16 -0
- package/dist/commands/assembly/add-gff.js +76 -0
- package/dist/commands/assembly/check.d.ts +15 -0
- package/dist/commands/assembly/check.js +148 -0
- package/dist/commands/assembly/delete.d.ts +14 -0
- package/dist/commands/assembly/delete.js +43 -0
- package/dist/commands/assembly/get.d.ts +9 -0
- package/dist/commands/assembly/get.js +33 -0
- package/dist/commands/assembly/get.test.d.ts +1 -0
- package/dist/commands/assembly/get.test.js +50 -0
- package/dist/commands/assembly/sequence.d.ts +16 -0
- package/dist/commands/assembly/sequence.js +117 -0
- package/dist/commands/change/get.d.ts +9 -0
- package/dist/commands/change/get.js +35 -0
- package/dist/commands/change/get.test.d.ts +1 -0
- package/dist/commands/change/get.test.js +22 -0
- package/dist/commands/config.d.ts +25 -0
- package/dist/commands/config.js +217 -0
- package/dist/commands/config.test.d.ts +1 -0
- package/dist/commands/config.test.js +188 -0
- package/dist/commands/feature/add-child.d.ts +17 -0
- package/dist/commands/feature/add-child.js +120 -0
- package/dist/commands/feature/check.d.ts +14 -0
- package/dist/commands/feature/check.js +97 -0
- package/dist/commands/feature/copy.d.ts +17 -0
- package/dist/commands/feature/copy.js +110 -0
- package/dist/commands/feature/delete.d.ts +11 -0
- package/dist/commands/feature/delete.js +99 -0
- package/dist/commands/feature/edit-attribute.d.ts +16 -0
- package/dist/commands/feature/edit-attribute.js +100 -0
- package/dist/commands/feature/edit-coords.d.ts +15 -0
- package/dist/commands/feature/edit-coords.js +109 -0
- package/dist/commands/feature/edit-type.d.ts +10 -0
- package/dist/commands/feature/edit-type.js +70 -0
- package/dist/commands/feature/edit.d.ts +13 -0
- package/dist/commands/feature/edit.js +81 -0
- package/dist/commands/feature/get-id.d.ts +14 -0
- package/dist/commands/feature/get-id.js +56 -0
- package/dist/commands/feature/get.d.ts +16 -0
- package/dist/commands/feature/get.js +87 -0
- package/dist/commands/feature/import.d.ts +15 -0
- package/dist/commands/feature/import.js +83 -0
- package/dist/commands/feature/search.d.ts +14 -0
- package/dist/commands/feature/search.js +91 -0
- package/dist/commands/login.d.ts +21 -0
- package/dist/commands/login.js +206 -0
- package/dist/commands/login.test.d.ts +1 -0
- package/dist/commands/login.test.js +52 -0
- package/dist/commands/logout.d.ts +10 -0
- package/dist/commands/logout.js +41 -0
- package/dist/commands/logout.test.d.ts +1 -0
- package/dist/commands/logout.test.js +67 -0
- package/dist/commands/refseq/get.d.ts +13 -0
- package/dist/commands/refseq/get.js +44 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.js +38 -0
- package/dist/commands/status.test.d.ts +1 -0
- package/dist/commands/status.test.js +54 -0
- package/dist/commands/user/get.d.ts +14 -0
- package/dist/commands/user/get.js +46 -0
- package/dist/commands/user/get.test.d.ts +1 -0
- package/dist/commands/user/get.test.js +23 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/test/fixtures.d.ts +7 -0
- package/dist/test/fixtures.js +40 -0
- package/dist/utils.d.ts +54 -0
- package/dist/utils.js +373 -0
- package/oclif.manifest.json +1632 -0
- package/package.json +100 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import input from '@inquirer/input';
|
|
4
|
+
import password from '@inquirer/password';
|
|
5
|
+
import select from '@inquirer/select';
|
|
6
|
+
import { Args, Flags } from '@oclif/core';
|
|
7
|
+
import { fetch } from 'undici';
|
|
8
|
+
import { BaseCommand } from '../baseCommand.js';
|
|
9
|
+
import { Config, ConfigError, KEYS, optionDesc } from '../Config.js';
|
|
10
|
+
import { createFetchErrorMessage, localhostToAddress, wrapLines, } from '../utils.js';
|
|
11
|
+
export default class ApolloConfig extends BaseCommand {
|
|
12
|
+
static summary = 'Get or set apollo configuration options';
|
|
13
|
+
static description = wrapLines(`Use this command to create or edit a user profile with credentials to access Apollo. \
|
|
14
|
+
Configuration options are:
|
|
15
|
+
|
|
16
|
+
${optionDesc().join('\n\n')}`);
|
|
17
|
+
static args = {
|
|
18
|
+
key: Args.string({
|
|
19
|
+
name: 'key',
|
|
20
|
+
description: 'Name of configuration parameter',
|
|
21
|
+
}),
|
|
22
|
+
value: Args.string({
|
|
23
|
+
name: 'value',
|
|
24
|
+
description: 'Parameter value',
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
static flags = {
|
|
28
|
+
profile: Flags.string({
|
|
29
|
+
description: 'Profile to create or edit',
|
|
30
|
+
required: false,
|
|
31
|
+
}),
|
|
32
|
+
'get-config-file': Flags.boolean({
|
|
33
|
+
description: 'Return the path to the config file and exit (this file may not exist yet)',
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
static examples = [
|
|
37
|
+
{
|
|
38
|
+
description: 'Interactive setup:',
|
|
39
|
+
command: '<%= config.bin %> <%= command.id %>',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
description: 'Setup with key/value pairs:',
|
|
43
|
+
command: '<%= config.bin %> <%= command.id %> --profile admin address http://localhost:3999',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
description: 'Get current address for default profile:',
|
|
47
|
+
command: '<%= config.bin %> <%= command.id %> address',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
async run() {
|
|
51
|
+
const { args } = await this.parse(ApolloConfig);
|
|
52
|
+
const { flags } = await this.parse(ApolloConfig);
|
|
53
|
+
let configFile = flags['config-file'];
|
|
54
|
+
configFile =
|
|
55
|
+
configFile === undefined
|
|
56
|
+
? path.join(this.config.configDir, 'config.yaml')
|
|
57
|
+
: path.resolve(configFile);
|
|
58
|
+
if (flags['get-config-file']) {
|
|
59
|
+
this.log(configFile);
|
|
60
|
+
this.exit(0);
|
|
61
|
+
}
|
|
62
|
+
const config = new Config(configFile);
|
|
63
|
+
if (args.key === undefined) {
|
|
64
|
+
await this.interactiveSetup(config, flags.profile);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
let profileName = flags.profile;
|
|
68
|
+
if (profileName === undefined) {
|
|
69
|
+
profileName = process.env.APOLLO_PROFILE ?? 'default';
|
|
70
|
+
}
|
|
71
|
+
if (flags.profile !== undefined) {
|
|
72
|
+
profileName = flags.profile;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
if (args.value === undefined) {
|
|
76
|
+
const currentValue = config.get(args.key, profileName);
|
|
77
|
+
this.log(currentValue);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
config.set(args.key, args.value, profileName);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error instanceof Error) {
|
|
85
|
+
this.logToStderr(error.message);
|
|
86
|
+
}
|
|
87
|
+
this.exit(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const v = config.validate().error?.message;
|
|
91
|
+
if (v) {
|
|
92
|
+
this.logToStderr(v);
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
config.writeConfigFile();
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error instanceof ConfigError) {
|
|
99
|
+
this.logToStderr(error.message);
|
|
100
|
+
this.exit(1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async interactiveSetup(config, profileName) {
|
|
105
|
+
if (profileName === undefined) {
|
|
106
|
+
profileName = await this.askProfileName(config.getProfileNames());
|
|
107
|
+
}
|
|
108
|
+
let setMe = true;
|
|
109
|
+
while (setMe) {
|
|
110
|
+
const address = await this.askAddress(config.get(KEYS[KEYS.address], profileName));
|
|
111
|
+
try {
|
|
112
|
+
config.set('address', address, profileName);
|
|
113
|
+
setMe = false;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (error instanceof ConfigError) {
|
|
117
|
+
this.logToStderr(error.message);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const address = config.get(KEYS[KEYS.address], profileName);
|
|
122
|
+
let accessType = '';
|
|
123
|
+
try {
|
|
124
|
+
accessType = await this.selectAccessType(address);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
if (error instanceof ConfigError) {
|
|
128
|
+
this.logToStderr(error.message);
|
|
129
|
+
this.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
config.set(KEYS[KEYS.accessType], accessType, profileName);
|
|
133
|
+
if (accessType === 'root') {
|
|
134
|
+
const username = await this.askUsername(config.get(KEYS.rootCredentials_username, profileName));
|
|
135
|
+
config.set('rootCredentials.username', username, profileName);
|
|
136
|
+
const password = await this.askPassword();
|
|
137
|
+
config.set('rootCredentials.password', password, profileName);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async askProfileName(currentProfiles) {
|
|
141
|
+
const newProfile = '<New profile>';
|
|
142
|
+
currentProfiles.push(newProfile);
|
|
143
|
+
const xchoices = [];
|
|
144
|
+
for (const profileName of currentProfiles) {
|
|
145
|
+
xchoices.push({ name: profileName, value: profileName });
|
|
146
|
+
}
|
|
147
|
+
let answer = newProfile;
|
|
148
|
+
if (currentProfiles.length > 1) {
|
|
149
|
+
answer = await select({
|
|
150
|
+
message: 'Select profile to edit or create a new one:',
|
|
151
|
+
choices: xchoices,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
if (answer === newProfile) {
|
|
155
|
+
answer = await input({
|
|
156
|
+
message: 'New profile name:',
|
|
157
|
+
default: currentProfiles.includes('default') ? undefined : 'default',
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return answer;
|
|
161
|
+
}
|
|
162
|
+
async askPassword() {
|
|
163
|
+
let answer = '';
|
|
164
|
+
while (answer === '') {
|
|
165
|
+
answer = await password({
|
|
166
|
+
message: 'Root password:',
|
|
167
|
+
mask: true,
|
|
168
|
+
});
|
|
169
|
+
answer = answer.trim();
|
|
170
|
+
}
|
|
171
|
+
return answer;
|
|
172
|
+
}
|
|
173
|
+
async askUsername(currentUsername) {
|
|
174
|
+
if (currentUsername === undefined || currentUsername.trim() === '') {
|
|
175
|
+
currentUsername = 'root';
|
|
176
|
+
}
|
|
177
|
+
const answer = await input({
|
|
178
|
+
message: 'Root username:',
|
|
179
|
+
default: currentUsername,
|
|
180
|
+
});
|
|
181
|
+
return answer.trim();
|
|
182
|
+
}
|
|
183
|
+
async askAddress(currentAddress) {
|
|
184
|
+
if (currentAddress === '' || currentAddress === undefined) {
|
|
185
|
+
currentAddress = 'http://localhost:3999';
|
|
186
|
+
}
|
|
187
|
+
const answer = await input({
|
|
188
|
+
message: 'Server address and port:',
|
|
189
|
+
default: currentAddress,
|
|
190
|
+
});
|
|
191
|
+
return answer.trim();
|
|
192
|
+
}
|
|
193
|
+
async selectAccessType(address) {
|
|
194
|
+
const xchoices = [{ name: 'root', value: 'root' }];
|
|
195
|
+
const types = await this.getLoginTypes(address);
|
|
196
|
+
for (const type of types) {
|
|
197
|
+
xchoices.push({ name: type, value: type });
|
|
198
|
+
}
|
|
199
|
+
const answer = await select({
|
|
200
|
+
message: 'Select login type',
|
|
201
|
+
choices: xchoices,
|
|
202
|
+
});
|
|
203
|
+
return answer;
|
|
204
|
+
}
|
|
205
|
+
async getLoginTypes(address) {
|
|
206
|
+
const response = await fetch(localhostToAddress(`${address}/auth/types`));
|
|
207
|
+
if (!response.ok) {
|
|
208
|
+
const errorMessage = await createFetchErrorMessage(response, `Unable to retrieve login types for address: "${address}"\n`);
|
|
209
|
+
throw new Error(errorMessage);
|
|
210
|
+
}
|
|
211
|
+
const dat = await response.json();
|
|
212
|
+
if (Array.isArray(dat)) {
|
|
213
|
+
return dat;
|
|
214
|
+
}
|
|
215
|
+
throw new Error(`Unexpected object: ${JSON.stringify(dat)}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { expect, test } from '@oclif/test';
|
|
7
|
+
import YAML from 'yaml';
|
|
8
|
+
import { CONFIG_FILE, TEST_DATA_DIR, VERBOSE, copyFile, } from '../test/fixtures.js';
|
|
9
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
10
|
+
describe('apollo config: Query config file', () => {
|
|
11
|
+
before(() => {
|
|
12
|
+
copyFile(`${TEST_DATA_DIR}/complete_config.yaml`, CONFIG_FILE, VERBOSE);
|
|
13
|
+
});
|
|
14
|
+
after(() => {
|
|
15
|
+
fs.rmSync(CONFIG_FILE);
|
|
16
|
+
});
|
|
17
|
+
let cmd = ['config', 'rootCredentials.password'];
|
|
18
|
+
test
|
|
19
|
+
.stdout()
|
|
20
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
21
|
+
.it(cmd.join(' '), (ctx) => {
|
|
22
|
+
expect(ctx.stdout.trim()).to.equal('1234');
|
|
23
|
+
});
|
|
24
|
+
cmd = ['config', 'address'];
|
|
25
|
+
test
|
|
26
|
+
.stdout()
|
|
27
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
28
|
+
.it(cmd.join(' '), (ctx) => {
|
|
29
|
+
expect(ctx.stdout.trim()).to.equal('http://localhost:3999');
|
|
30
|
+
});
|
|
31
|
+
cmd = ['config', 'someInvalidKey'];
|
|
32
|
+
test
|
|
33
|
+
.stderr()
|
|
34
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
35
|
+
.exit(1)
|
|
36
|
+
.do((output) => expect(output.stderr).to.contain('someInvalidKey'))
|
|
37
|
+
.it(cmd.join(' '));
|
|
38
|
+
cmd = ['config', '--profile', 'profile1', 'address'];
|
|
39
|
+
test
|
|
40
|
+
.stdout()
|
|
41
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
42
|
+
.it(cmd.join(' '), (ctx) => {
|
|
43
|
+
expect(ctx.stdout.trim()).to.equal('http://localhost:1999');
|
|
44
|
+
});
|
|
45
|
+
cmd = ['config', '--profile', 'profile1', 'rootCredentials.username'];
|
|
46
|
+
test
|
|
47
|
+
.stdout()
|
|
48
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
49
|
+
.it(cmd.join(' '), (ctx) => {
|
|
50
|
+
expect(ctx.stdout.trim()).to.equal('');
|
|
51
|
+
});
|
|
52
|
+
cmd = ['config', '--profile', 'nonExistantProfile', 'address'];
|
|
53
|
+
test
|
|
54
|
+
.stdout()
|
|
55
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
56
|
+
.it(cmd.join(' '), (ctx) => {
|
|
57
|
+
expect(ctx.stdout.trim()).to.equal('');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('apollo config: Missing config file', () => {
|
|
61
|
+
let cmd = ['config', 'rootCredentials.password'];
|
|
62
|
+
test
|
|
63
|
+
.stdout()
|
|
64
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
65
|
+
.it(cmd.join(' '), (ctx) => {
|
|
66
|
+
expect(ctx.stdout.trim()).to.equal('');
|
|
67
|
+
});
|
|
68
|
+
cmd = ['config', 'address'];
|
|
69
|
+
test
|
|
70
|
+
.stdout()
|
|
71
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
72
|
+
.it(cmd.join(' '), (ctx) => {
|
|
73
|
+
expect(ctx.stdout.trim()).to.equal('');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('apollo config: Create config directory', () => {
|
|
77
|
+
const outdir = `${TEST_DATA_DIR}/_tmp`;
|
|
78
|
+
before(() => {
|
|
79
|
+
fs.rmSync(outdir, { recursive: true, force: true });
|
|
80
|
+
});
|
|
81
|
+
after(() => {
|
|
82
|
+
fs.rmSync(outdir, { recursive: true, force: true });
|
|
83
|
+
});
|
|
84
|
+
const cmd = [
|
|
85
|
+
'config',
|
|
86
|
+
'--config-file',
|
|
87
|
+
`${outdir}/config.yaml`,
|
|
88
|
+
'address',
|
|
89
|
+
'http://localhost:3999',
|
|
90
|
+
];
|
|
91
|
+
test
|
|
92
|
+
.stdout()
|
|
93
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
94
|
+
.it(cmd.join(' '), () => {
|
|
95
|
+
expect(fs.existsSync(`${outdir}/config.yaml`));
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe('apollo config: Read & edit config file', () => {
|
|
99
|
+
before(() => {
|
|
100
|
+
copyFile(`${TEST_DATA_DIR}/complete_config.yaml`, CONFIG_FILE, VERBOSE);
|
|
101
|
+
});
|
|
102
|
+
after(() => {
|
|
103
|
+
fs.rmSync(CONFIG_FILE);
|
|
104
|
+
});
|
|
105
|
+
let cmd = ['config', 'address', 'http://localhost:4321'];
|
|
106
|
+
test
|
|
107
|
+
.stdout()
|
|
108
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
109
|
+
.it(cmd.join(' '), () => {
|
|
110
|
+
const cfg = YAML.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
111
|
+
expect(cfg.default.address).to.equal('http://localhost:4321');
|
|
112
|
+
expect(cfg.default.rootCredentials.username).to.equal('root');
|
|
113
|
+
});
|
|
114
|
+
cmd = ['config', 'rootCredentials.username', 'root2'];
|
|
115
|
+
test
|
|
116
|
+
.stdout()
|
|
117
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
118
|
+
.it(cmd.join(' '), () => {
|
|
119
|
+
const cfg = YAML.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
120
|
+
expect(cfg.default.rootCredentials.username).to.equal('root2');
|
|
121
|
+
});
|
|
122
|
+
cmd = ['config', 'accessType', 'microsoft'];
|
|
123
|
+
test
|
|
124
|
+
.stdout()
|
|
125
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
126
|
+
.it(cmd.join(' '), () => {
|
|
127
|
+
const cfg = YAML.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
128
|
+
expect(cfg.default.accessType).to.equal('microsoft');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe('Do not set invalid address', () => {
|
|
132
|
+
before(() => {
|
|
133
|
+
copyFile(`${TEST_DATA_DIR}/complete_config.yaml`, CONFIG_FILE, VERBOSE);
|
|
134
|
+
});
|
|
135
|
+
after(() => {
|
|
136
|
+
fs.rmSync(CONFIG_FILE);
|
|
137
|
+
});
|
|
138
|
+
const cmd = ['config', 'address', 'http://localhost:3999xxx'];
|
|
139
|
+
test
|
|
140
|
+
.stderr()
|
|
141
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
142
|
+
.exit(1)
|
|
143
|
+
.do((output) => expect(output.stderr).to.contain('http://localhost:3999xxx'))
|
|
144
|
+
.it(cmd.join(' '));
|
|
145
|
+
});
|
|
146
|
+
describe('apollo config: Write config file from scratch', () => {
|
|
147
|
+
after(() => {
|
|
148
|
+
fs.rmSync(CONFIG_FILE);
|
|
149
|
+
});
|
|
150
|
+
let cmd = ['config', 'rootCredentials.username', 'somename'];
|
|
151
|
+
test
|
|
152
|
+
.stdout()
|
|
153
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
154
|
+
.it(cmd.join(' '), () => {
|
|
155
|
+
const cfg = YAML.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
156
|
+
expect(cfg.default.rootCredentials.username).to.equal('somename');
|
|
157
|
+
});
|
|
158
|
+
cmd = [
|
|
159
|
+
'config',
|
|
160
|
+
'--profile',
|
|
161
|
+
'myProfile',
|
|
162
|
+
'rootCredentials.password',
|
|
163
|
+
'somepwd',
|
|
164
|
+
];
|
|
165
|
+
test
|
|
166
|
+
.stdout()
|
|
167
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
168
|
+
.it(cmd.join(' '), () => {
|
|
169
|
+
const cfg = YAML.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
170
|
+
expect(cfg.myProfile.rootCredentials.password).to.equal('somepwd');
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
describe('apollo config: Handle strings as numbers in yaml', () => {
|
|
174
|
+
const cmd = [
|
|
175
|
+
'config',
|
|
176
|
+
'--config-file',
|
|
177
|
+
'test_data/nameAsNumber.yaml',
|
|
178
|
+
'rootCredentials.username',
|
|
179
|
+
];
|
|
180
|
+
test
|
|
181
|
+
.stderr()
|
|
182
|
+
.stdout()
|
|
183
|
+
.command(cmd, { root: dirname(dirname(__dirname)) })
|
|
184
|
+
.do((output) => expect(output.stderr).contains('must be a string'))
|
|
185
|
+
.it(cmd.join(' '), (ctx) => {
|
|
186
|
+
expect(ctx.stdout.trim()).to.equal('7890');
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BaseCommand } from '../../baseCommand.js';
|
|
2
|
+
export default class Get extends BaseCommand<typeof Get> {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: {
|
|
6
|
+
description: string;
|
|
7
|
+
command: string;
|
|
8
|
+
}[];
|
|
9
|
+
static flags: {
|
|
10
|
+
'feature-id': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
11
|
+
start: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
12
|
+
end: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
13
|
+
type: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
private addChild;
|
|
17
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
|
2
|
+
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
5
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
6
|
+
import { Flags } from '@oclif/core';
|
|
7
|
+
import { ObjectId } from 'bson';
|
|
8
|
+
import { fetch } from 'undici';
|
|
9
|
+
import { BaseCommand } from '../../baseCommand.js';
|
|
10
|
+
import { createFetchErrorMessage, getFeatureById, idReader, localhostToAddress, queryApollo, wrapLines, } from '../../utils.js';
|
|
11
|
+
export default class Get extends BaseCommand {
|
|
12
|
+
static summary = 'Add a child feature (e.g. add an exon to an mRNA)';
|
|
13
|
+
static description = wrapLines('See the other commands under `apollo feature` to retrive the parent ID of interest and to populate the child feature with attributes.');
|
|
14
|
+
static examples = [
|
|
15
|
+
{
|
|
16
|
+
description: 'Add an exon at genomic coordinates 10..20 to this feature ID:',
|
|
17
|
+
command: '<%= config.bin %> <%= command.id %> -i 6605826fbd0eee691f83e73f -t exon -s 10 -e 20',
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
static flags = {
|
|
21
|
+
'feature-id': Flags.string({
|
|
22
|
+
char: 'i',
|
|
23
|
+
default: '-',
|
|
24
|
+
description: 'Add a child to this feature ID; use - to read it from stdin',
|
|
25
|
+
}),
|
|
26
|
+
start: Flags.integer({
|
|
27
|
+
char: 's',
|
|
28
|
+
required: true,
|
|
29
|
+
description: 'Start coordinate of the child feature (1-based)',
|
|
30
|
+
}),
|
|
31
|
+
end: Flags.integer({
|
|
32
|
+
char: 'e',
|
|
33
|
+
required: true,
|
|
34
|
+
description: 'End coordinate of the child feature (1-based)',
|
|
35
|
+
}),
|
|
36
|
+
type: Flags.string({
|
|
37
|
+
char: 't',
|
|
38
|
+
required: true,
|
|
39
|
+
description: 'Type of child feature',
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
async run() {
|
|
43
|
+
const { flags } = await this.parse(Get);
|
|
44
|
+
if (flags.end < flags.start) {
|
|
45
|
+
this.logToStderr('Error: End coordinate is lower than the start coordinate');
|
|
46
|
+
this.exit(1);
|
|
47
|
+
}
|
|
48
|
+
if (flags.start <= 0) {
|
|
49
|
+
this.logToStderr('Coordinates must be greater than 0');
|
|
50
|
+
this.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const ff = idReader([flags['feature-id']]);
|
|
53
|
+
if (ff.length !== 1) {
|
|
54
|
+
this.logToStderr(`Expected only one feature identifier. Got ${ff.length}`);
|
|
55
|
+
}
|
|
56
|
+
const [featureId] = ff;
|
|
57
|
+
const access = await this.getAccess(flags['config-file'], flags.profile);
|
|
58
|
+
const response = await getFeatureById(access.address, access.accessToken, featureId);
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
const errorMessage = await createFetchErrorMessage(response, 'getFeatureById failed');
|
|
61
|
+
throw new Error(errorMessage);
|
|
62
|
+
}
|
|
63
|
+
const feature = JSON.parse(await response.text());
|
|
64
|
+
const childRes = await this.addChild(access.address, access.accessToken, feature, flags.start - 1, flags.end, flags.type);
|
|
65
|
+
if (!childRes.ok) {
|
|
66
|
+
const obj = JSON.parse(await response.text());
|
|
67
|
+
const message = obj['message'];
|
|
68
|
+
this.logToStderr(message);
|
|
69
|
+
this.exit(1);
|
|
70
|
+
}
|
|
71
|
+
this.exit(0);
|
|
72
|
+
}
|
|
73
|
+
async addChild(address, accessToken, parentFeature, start, end, type) {
|
|
74
|
+
const pStart = parentFeature['start'];
|
|
75
|
+
const pEnd = parentFeature['end'];
|
|
76
|
+
if (start < pStart || end > pEnd) {
|
|
77
|
+
this.logToStderr(`Error: Child feature coordinates (${start + 1}-${end}) cannot extend beyond parent coordinates (${pStart + 1}-${pEnd})`);
|
|
78
|
+
this.exit(1);
|
|
79
|
+
}
|
|
80
|
+
const res = await queryApollo(address, accessToken, 'refSeqs');
|
|
81
|
+
const refSeqs = (await res.json());
|
|
82
|
+
const refSeq = parentFeature['refSeq'];
|
|
83
|
+
let assembly = '';
|
|
84
|
+
for (const x of refSeqs) {
|
|
85
|
+
if (x['_id'] === refSeq) {
|
|
86
|
+
assembly = x['assembly'];
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const change = {
|
|
91
|
+
typeName: 'AddFeatureChange',
|
|
92
|
+
changedIds: [parentFeature['_id']],
|
|
93
|
+
assembly,
|
|
94
|
+
addedFeature: {
|
|
95
|
+
_id: new ObjectId().toHexString(),
|
|
96
|
+
gffId: '',
|
|
97
|
+
refSeq,
|
|
98
|
+
start,
|
|
99
|
+
end,
|
|
100
|
+
type,
|
|
101
|
+
},
|
|
102
|
+
parentFeatureId: parentFeature['_id'],
|
|
103
|
+
};
|
|
104
|
+
const url = new URL(localhostToAddress(`${address}/changes`));
|
|
105
|
+
const auth = {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
body: JSON.stringify(change),
|
|
108
|
+
headers: {
|
|
109
|
+
authorization: `Bearer ${accessToken}`,
|
|
110
|
+
'Content-Type': 'application/json',
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
const response = await fetch(url, auth);
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
const errorMessage = await createFetchErrorMessage(response, 'getFeatureById failed');
|
|
116
|
+
throw new Error(errorMessage);
|
|
117
|
+
}
|
|
118
|
+
return response;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BaseCommand } from '../../baseCommand.js';
|
|
2
|
+
export default class Check extends BaseCommand<typeof Check> {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: {
|
|
6
|
+
description: string;
|
|
7
|
+
command: string;
|
|
8
|
+
}[];
|
|
9
|
+
static flags: {
|
|
10
|
+
'feature-id': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
11
|
+
assembly: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
12
|
+
};
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { fetch } from 'undici';
|
|
3
|
+
import { BaseCommand } from '../../baseCommand.js';
|
|
4
|
+
import { convertAssemblyNameToId, createFetchErrorMessage, idReader, localhostToAddress, queryApollo, wrapLines, } from '../../utils.js';
|
|
5
|
+
export default class Check extends BaseCommand {
|
|
6
|
+
static summary = 'Get check results';
|
|
7
|
+
static description = wrapLines('Use this command to view which features fail checks along with the reason for failing.\
|
|
8
|
+
Use `apollo assembly check` for managing which checks should be applied to an assembly');
|
|
9
|
+
static examples = [
|
|
10
|
+
{
|
|
11
|
+
description: 'Get all check results in the database:',
|
|
12
|
+
command: '<%= config.bin %> <%= command.id %>',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
description: 'Get check results for assembly hg19:',
|
|
16
|
+
command: '<%= config.bin %> <%= command.id %> -a hg19',
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
static flags = {
|
|
20
|
+
'feature-id': Flags.string({
|
|
21
|
+
char: 'i',
|
|
22
|
+
description: 'Get checks for these feature identifiers',
|
|
23
|
+
multiple: true,
|
|
24
|
+
}),
|
|
25
|
+
assembly: Flags.string({
|
|
26
|
+
char: 'a',
|
|
27
|
+
description: 'Get checks for this assembly',
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
async run() {
|
|
31
|
+
const { flags } = await this.parse(Check);
|
|
32
|
+
const access = await this.getAccess(flags['config-file'], flags.profile);
|
|
33
|
+
let keepFeatures = new Set();
|
|
34
|
+
if (flags['feature-id'] !== undefined) {
|
|
35
|
+
keepFeatures = new Set(idReader(flags['feature-id']));
|
|
36
|
+
}
|
|
37
|
+
const keepAsmId = await keepAssemblies(access.address, access.accessToken, flags.assembly);
|
|
38
|
+
const res = await queryApollo(access.address, access.accessToken, 'refseqs');
|
|
39
|
+
const refseq = (await res.json());
|
|
40
|
+
const refseqId = new Set();
|
|
41
|
+
for (const x of refseq) {
|
|
42
|
+
if (keepAsmId.includes(x['assembly'])) {
|
|
43
|
+
refseqId.add(x['_id']);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const checks = await getChecks(access.address, access.accessToken);
|
|
47
|
+
const results = [];
|
|
48
|
+
for (const chk of checks) {
|
|
49
|
+
let keep = false;
|
|
50
|
+
if (flags['feature-id'] === undefined) {
|
|
51
|
+
keep = true;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
for (const x of chk['ids']) {
|
|
55
|
+
if (keepFeatures.has(x)) {
|
|
56
|
+
keep = true;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (keep && refseqId.has(chk['refSeq'])) {
|
|
62
|
+
results.push(chk);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
this.log(JSON.stringify(results, null, 2));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function keepAssemblies(address, accessToken, assembly) {
|
|
69
|
+
let keepAssembly = [];
|
|
70
|
+
if (assembly === undefined) {
|
|
71
|
+
const res = await queryApollo(address, accessToken, 'assemblies');
|
|
72
|
+
const asm = (await res.json());
|
|
73
|
+
for (const x of asm) {
|
|
74
|
+
keepAssembly.push(x['_id']);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
const ids = idReader([assembly]);
|
|
79
|
+
keepAssembly = await convertAssemblyNameToId(address, accessToken, ids);
|
|
80
|
+
}
|
|
81
|
+
return keepAssembly;
|
|
82
|
+
}
|
|
83
|
+
async function getChecks(address, token) {
|
|
84
|
+
const url = new URL(localhostToAddress(`${address}/checks`));
|
|
85
|
+
const auth = {
|
|
86
|
+
headers: {
|
|
87
|
+
authorization: `Bearer ${token}`,
|
|
88
|
+
'Content-Type': 'application/json',
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
const response = await fetch(url, auth);
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
const errorMessage = await createFetchErrorMessage(response, 'Failed to access Apollo with the current address and/or access token\nThe server returned:\n');
|
|
94
|
+
throw new Error(errorMessage);
|
|
95
|
+
}
|
|
96
|
+
return (await response.json());
|
|
97
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BaseCommand } from '../../baseCommand.js';
|
|
2
|
+
export default class Copy extends BaseCommand<typeof Copy> {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: {
|
|
6
|
+
description: string;
|
|
7
|
+
command: string;
|
|
8
|
+
}[];
|
|
9
|
+
static flags: {
|
|
10
|
+
'feature-id': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
11
|
+
refseq: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
12
|
+
start: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
13
|
+
assembly: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
private copyFeature;
|
|
17
|
+
}
|