cob-cli 2.49.0 → 2.50.0
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/bin/cob-cli.js +31 -0
- package/lib/commands/cleanup.js +106 -0
- package/lib/commands/deploy.js +9 -0
- package/lib/commands/package.js +20 -0
- package/lib/commands/tagDefs.js +67 -0
- package/lib/commands/test.js +27 -9
- package/lib/commands/updateFromServer.js +149 -13
- package/lib/task_lists/common_definitions.js +26 -0
- package/lib/task_lists/common_enviromentHandler.js +5 -5
- package/lib/task_lists/common_helpers.js +59 -13
- package/lib/task_lists/common_operationState.js +28 -0
- package/lib/task_lists/common_syncFiles.js +4 -0
- package/lib/task_lists/package_execute.js +45 -0
- package/lib/task_lists/tagDefs_execute.js +121 -0
- package/lib/task_lists/test_customUIsContinuosReload.js +36 -7
- package/lib/task_lists/test_otherFilesContiousReload.js +37 -16
- package/lib/task_lists/test_syncFile.js +1 -0
- package/lib/task_lists/updateFromServer_definitions.js +135 -0
- package/lib/task_lists/updateFromServer_domains.js +50 -0
- package/lib/task_lists/updateFromServer_groups.js +111 -0
- package/lib/task_lists/updateFromServer_instances.js +76 -0
- package/lib/task_lists/updateFromServer_kibana.js +88 -0
- package/lib/task_lists/updateFromServer_validate.js +3 -3
- package/lib/webpack/webpack.config.js +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
require('colors');
|
|
2
|
+
const Listr = require('listr');
|
|
3
|
+
const UpdaterRenderer = require('listr-update-renderer');
|
|
4
|
+
const VerboseRenderer = require('listr-verbose-renderer');
|
|
5
|
+
const axios = require("axios");
|
|
6
|
+
const fs = require("fs/promises");
|
|
7
|
+
|
|
8
|
+
async function getGroups(query, servername) {
|
|
9
|
+
|
|
10
|
+
const uri = `https://${servername}.cultofbits.com/userm/userm/search/userm/group?`
|
|
11
|
+
const response = await axios.get( uri + new URLSearchParams({
|
|
12
|
+
q: query,
|
|
13
|
+
size: "100" // probably excessive
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
if(response.status != 200){
|
|
17
|
+
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);
|
|
18
|
+
}
|
|
19
|
+
const groupIds = response.data.hits.hits.map((h) => h._id)
|
|
20
|
+
return groupIds;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const sortByName = (a, b) => a.name?.localeCompare(b.name);
|
|
24
|
+
|
|
25
|
+
const exportGroups = async function(ids, servername, exportPath){
|
|
26
|
+
|
|
27
|
+
const uri = `https://${servername}.cultofbits.com/userm/userm/group/export/${ids.join(',')}`
|
|
28
|
+
|
|
29
|
+
const response = await axios.get( uri );
|
|
30
|
+
if(response.status != 200 ){
|
|
31
|
+
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);
|
|
32
|
+
}
|
|
33
|
+
const data = response.data;
|
|
34
|
+
|
|
35
|
+
// sort everything to be easier to diff
|
|
36
|
+
data.groups?.sort(sortByName);
|
|
37
|
+
data.groups?.forEach(group => group.roles?.sort())
|
|
38
|
+
data.roles?.sort(sortByName);
|
|
39
|
+
data.roles?.forEach(role => role.perms?.sort())
|
|
40
|
+
data.perms?.sort(sortByName)
|
|
41
|
+
const re = /:(\d+)/g
|
|
42
|
+
defnameCache.servername = servername;
|
|
43
|
+
|
|
44
|
+
for(r of data.roles) {
|
|
45
|
+
for(let i = 0; i < r.perms.length; i++){
|
|
46
|
+
r.perms[i] = await replaceAsync(r.perms[i], re, replaceIdsWithName)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for(p of data.perms){
|
|
50
|
+
if( p.product == 'recordm') {
|
|
51
|
+
p.name = await replaceAsync(p.name, re, replaceIdsWithName)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
fs.writeFile(exportPath, JSON.stringify(data, null, 2))
|
|
56
|
+
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
async function replaceIdsWithName(_m, g1){
|
|
60
|
+
return ":" + ( "§§" + await defnameCache.get(g1) + "§§")
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function getGroupsFromServer(servername, groupsQueries, exportPath, customization, args) {
|
|
64
|
+
const searchTasks = groupsQueries.map( gp =>
|
|
65
|
+
({ title: `(${customization}) Permissions: `.bold + "searching for groups with query: " + gp,
|
|
66
|
+
task: async ctx => ctx.groupIds = ctx.groupIds
|
|
67
|
+
? ctx.groupIds.concat(await getGroups(gp, servername))
|
|
68
|
+
: await getGroups(gp, servername) }))
|
|
69
|
+
|
|
70
|
+
await new Listr(
|
|
71
|
+
[
|
|
72
|
+
...searchTasks,
|
|
73
|
+
{
|
|
74
|
+
title: `(${customization}) Permissions: `.bold + "exporting groups and subsequent roles/permissions",
|
|
75
|
+
task: ctx => exportGroups(ctx.groupIds, servername, exportPath)
|
|
76
|
+
},
|
|
77
|
+
],{
|
|
78
|
+
renderer: args.verbose ? VerboseRenderer : UpdaterRenderer,
|
|
79
|
+
collapse: false
|
|
80
|
+
})
|
|
81
|
+
.run()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const defnameCache = {
|
|
85
|
+
servername: '',
|
|
86
|
+
resolved: new Map(),
|
|
87
|
+
get: async function(id) {
|
|
88
|
+
if(!this.resolved.has(id)){
|
|
89
|
+
const uri = `https://${this.servername}.cultofbits.com/recordm/recordm/definitions/${id}`
|
|
90
|
+
const response = await axios.get( uri );
|
|
91
|
+
|
|
92
|
+
if(response.status != 200){
|
|
93
|
+
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.resolved.set(id, response.data.name)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return this.resolved.get(id)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function replaceAsync(string, regexp, replacerFunction) {
|
|
104
|
+
const replacements = await Promise.all(
|
|
105
|
+
Array.from(string.matchAll(regexp),
|
|
106
|
+
match => replacerFunction(...match)));
|
|
107
|
+
let i = 0;
|
|
108
|
+
return string.replace(regexp, () => replacements[i++]);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
exports.getGroupsFromServer = getGroupsFromServer
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require('colors');
|
|
2
|
+
const Listr = require('listr');
|
|
3
|
+
const UpdaterRenderer = require('listr-update-renderer');
|
|
4
|
+
const VerboseRenderer = require('listr-verbose-renderer');
|
|
5
|
+
const axios = require("axios");
|
|
6
|
+
const fs = require("fs/promises");
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async function getInstancesFromServer(servername, definitionsForInstances, rootPath, customization, args) {
|
|
10
|
+
|
|
11
|
+
const taskList = definitionsForInstances.map( def => ({
|
|
12
|
+
title: `(${customization}) Instances: `.bold + `Exporting instances from definition ${def.definition}`,
|
|
13
|
+
task: async () => getInstancesOfDefinition(servername, await getDefinition(servername, def.definition), def.query, def.crop, rootPath)
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
taskList.push({
|
|
17
|
+
title: `(${customization}) Instances: `.bold + 'Exporting _index',
|
|
18
|
+
task: async () => await exportIndex(definitionsForInstances, rootPath + "/_index")
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
await new Listr(taskList , {
|
|
22
|
+
renderer: args.verbose ? VerboseRenderer : UpdaterRenderer,
|
|
23
|
+
collapse: false
|
|
24
|
+
}).run()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function flattenFields(fieldArray) {
|
|
28
|
+
if (!fieldArray || fieldArray.length == 0)
|
|
29
|
+
return []
|
|
30
|
+
|
|
31
|
+
return fieldArray.flatMap(field => flattenFields(field.fields).concat([field]))
|
|
32
|
+
}
|
|
33
|
+
function getFieldList(fields) {
|
|
34
|
+
return Array.from(new Set(fields.map(f => f.name))).join(',')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async function getInstancesOfDefinition(servername, definition, query, crop, rootPath) {
|
|
39
|
+
|
|
40
|
+
const definitionId = definition.id
|
|
41
|
+
const fields = flattenFields(definition.fieldDefinitions)
|
|
42
|
+
|
|
43
|
+
if(!fields.some(f => (f.description || '').includes('$instanceLabel'))){
|
|
44
|
+
throw new Error("Should not export instances of a Definition without an $instanceLabel field, it would generate duplicates on import")
|
|
45
|
+
}
|
|
46
|
+
if(crop && !fields.some(f => (f.description || '').includes('$importer.last'))){
|
|
47
|
+
throw new Error("Can not crop instances of a Definition without an $importer.last field")
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const uri = `https://${servername}.cultofbits.com/recordm/recordm/definitions/search/download/${definitionId}?`
|
|
51
|
+
const response = await axios.get(uri + new URLSearchParams({
|
|
52
|
+
q: query,
|
|
53
|
+
vcn: "id," + getFieldList(fields)
|
|
54
|
+
}), {
|
|
55
|
+
responseType: 'arraybuffer',
|
|
56
|
+
headers: {'export-files': 'true'}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
fs.writeFile(rootPath + `${definition.name}.xlsx`, response.data)
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function getDefinition(servername, definitionName) {
|
|
64
|
+
|
|
65
|
+
const uri = `https://${servername}.cultofbits.com/recordm/recordm/definitions/name/${encodeURIComponent(definitionName)}`
|
|
66
|
+
const response = await axios.get(uri);
|
|
67
|
+
|
|
68
|
+
return response.data
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function exportIndex(defs, dataPath) {
|
|
72
|
+
await fs.writeFile(dataPath, defs.map(d => d.definition + ":" + d.crop).join('\n'), 'utf-8')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
exports.getInstancesFromServer = getInstancesFromServer
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require('colors');
|
|
2
|
+
const Listr = require('listr');
|
|
3
|
+
const UpdaterRenderer = require('listr-update-renderer');
|
|
4
|
+
const VerboseRenderer = require('listr-verbose-renderer');
|
|
5
|
+
const axios = require("axios");
|
|
6
|
+
const fs = require("fs/promises");
|
|
7
|
+
|
|
8
|
+
const KIBANA_TYPES = ['config','url','index-pattern','query','tag','canvas-element','canvas-workpad','action','alert','visualization','dashboard','map','lens','cases','search'];
|
|
9
|
+
|
|
10
|
+
async function exportSavedObjectsOfSpace(servername, space, filePath, args) {
|
|
11
|
+
const uri = `https://${servername}.cultofbits.com/kibana/s/${space.identifier}/api/saved_objects/_export`
|
|
12
|
+
try {
|
|
13
|
+
const response = await axios.post(uri, {
|
|
14
|
+
type: KIBANA_TYPES,
|
|
15
|
+
excludeExportDetails: true,
|
|
16
|
+
}, {
|
|
17
|
+
headers: { 'kbn-xsrf': 'true' },
|
|
18
|
+
responseType: 'text',
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const allObjects = response.data
|
|
22
|
+
.split('\n')
|
|
23
|
+
.filter(line => line.trim())
|
|
24
|
+
.map(line => JSON.parse(line));
|
|
25
|
+
|
|
26
|
+
await Promise.all(allObjects
|
|
27
|
+
.filter(e => e.type === 'index-pattern')
|
|
28
|
+
.map(async element => await replaceIndexPatternsIds(servername, element))
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
await fs.writeFile(filePath, allObjects.map(o => JSON.stringify(o)).join('\n'))
|
|
32
|
+
} catch(err) {
|
|
33
|
+
if(args.verbose > 0) {
|
|
34
|
+
console.error("error downloading from kibana {status:", err.response?.status, ", uri:", uri)
|
|
35
|
+
}
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function exportIndex(spaces, dataPath) {
|
|
41
|
+
await fs.writeFile(dataPath, spaces.map(s => s.identifier + ":" + s.name).join('\n'), 'utf-8')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
// replaces in place
|
|
46
|
+
async function replaceIndexPatternsIds(servername, indexPattern) {
|
|
47
|
+
if(indexPattern.type !== "index-pattern"){
|
|
48
|
+
throw new Error("Not an index pattern")
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const definitionId = indexPattern.attributes.title.substring('recordm-'.length)
|
|
52
|
+
const name = await getDefinitionName(servername, definitionId)
|
|
53
|
+
|
|
54
|
+
indexPattern.attributes.title = `recordm-§§${name}§§`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function getDefinitionName(servername, id) {
|
|
58
|
+
const uri = `https://${servername}.cultofbits.com/recordm/recordm/definitions/${id}`
|
|
59
|
+
const response = await axios.get( uri );
|
|
60
|
+
|
|
61
|
+
if(response.status != 200){
|
|
62
|
+
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return response.data.name
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function getKibanaFromServer(servername, spaces, roothpath, customization, args) {
|
|
69
|
+
|
|
70
|
+
const taskList = spaces.map( space => (
|
|
71
|
+
{
|
|
72
|
+
title: `(${customization}) Kibana: `.bold + `Exporting ${space.name}`,
|
|
73
|
+
task: async () => await exportSavedObjectsOfSpace(servername, space, roothpath + `/${space.name}.ndjson`, args)
|
|
74
|
+
}
|
|
75
|
+
))
|
|
76
|
+
|
|
77
|
+
taskList.push({
|
|
78
|
+
title: `(${customization}) Kibana: `.bold + 'Exporting _index',
|
|
79
|
+
task: async () => await exportIndex(spaces, roothpath + "/_index")
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
await new Listr( taskList, {
|
|
83
|
+
renderer: args.verbose ? VerboseRenderer : UpdaterRenderer,
|
|
84
|
+
collapse: false
|
|
85
|
+
}).run()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
exports.getKibanaFromServer = getKibanaFromServer
|
|
@@ -8,7 +8,7 @@ function validateUpdateFromServerConditions(cmdEnv) {
|
|
|
8
8
|
return new Listr([
|
|
9
9
|
{ title: "Check connectivity and permissions".bold, task: () => checkConnectivity(cmdEnv.server) },
|
|
10
10
|
{ title: "Check there's no cob-cli test' running".bold, task: () => checkNoTestsRunningOnServer(cmdEnv.server) },
|
|
11
|
-
{ title: "Check git status".bold, task: () => checkWorkingCopyCleanliness() },
|
|
12
|
-
|
|
11
|
+
{ title: "Check git status".bold, task: () => checkWorkingCopyCleanliness() },
|
|
12
|
+
])
|
|
13
13
|
}
|
|
14
|
-
exports.validateUpdateFromServerConditions = validateUpdateFromServerConditions
|
|
14
|
+
exports.validateUpdateFromServerConditions = validateUpdateFromServerConditions
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cob-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.50.0",
|
|
4
4
|
"description": "A command line utility to help Cult of Bits partners develop with higher speed and reusing common code and best practices.",
|
|
5
5
|
"preferGlobal": true,
|
|
6
6
|
"repository": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@webpack-cli/serve": "^2.0.1",
|
|
20
20
|
"axios": "^0.21.1",
|
|
21
|
+
"axios-cookiejar-support": "^5.0.5",
|
|
21
22
|
"chokidar": "^3.5.3",
|
|
22
23
|
"cmd-line-importer": "^1.0.0",
|
|
23
24
|
"colors": "^1.4.0",
|
|
@@ -28,11 +29,14 @@
|
|
|
28
29
|
"file-cookie-store": "^0.2.1",
|
|
29
30
|
"fs-extra": "^9.0.1",
|
|
30
31
|
"inquirer": "^7.3.3",
|
|
32
|
+
"js-yaml": "^4.1.0",
|
|
31
33
|
"listr": "^0.14.3",
|
|
32
34
|
"ncp": "^2.0.0",
|
|
33
35
|
"omelette": "^0.4.15-1",
|
|
34
36
|
"open": "^8.4.0",
|
|
35
37
|
"simple-git": "^3.15.1",
|
|
38
|
+
"tar": "^7.4.3",
|
|
39
|
+
"tough-cookie": "^5.1.2",
|
|
36
40
|
"webpack": "^5.75.0",
|
|
37
41
|
"webpack-cli": "^5.0.1",
|
|
38
42
|
"webpack-dev-server": "^4.11.1",
|