@jupiterone/integration-sdk-cli 9.5.0 → 9.6.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.
Files changed (122) hide show
  1. package/dist/src/commands/generate.d.ts +1 -0
  2. package/dist/src/commands/generate.js +29 -0
  3. package/dist/src/commands/generate.js.map +1 -0
  4. package/dist/src/commands/index.d.ts +1 -0
  5. package/dist/src/commands/index.js +1 -0
  6. package/dist/src/commands/index.js.map +1 -1
  7. package/dist/src/generator/actions.d.ts +4 -0
  8. package/dist/src/generator/actions.js +38 -0
  9. package/dist/src/generator/actions.js.map +1 -0
  10. package/dist/src/generator/configFieldsFlow.d.ts +7 -0
  11. package/dist/src/generator/configFieldsFlow.js +53 -0
  12. package/dist/src/generator/configFieldsFlow.js.map +1 -0
  13. package/dist/src/generator/entitiesFlow.d.ts +7 -0
  14. package/dist/src/generator/entitiesFlow.js +175 -0
  15. package/dist/src/generator/entitiesFlow.js.map +1 -0
  16. package/dist/src/generator/helpers.d.ts +2 -0
  17. package/dist/src/generator/helpers.js +9 -0
  18. package/dist/src/generator/helpers.js.map +1 -0
  19. package/dist/src/generator/newIntegration.d.ts +1 -0
  20. package/dist/src/generator/newIntegration.js +124 -0
  21. package/dist/src/generator/newIntegration.js.map +1 -0
  22. package/dist/src/generator/relationshipsFlow.d.ts +7 -0
  23. package/dist/src/generator/relationshipsFlow.js +65 -0
  24. package/dist/src/generator/relationshipsFlow.js.map +1 -0
  25. package/dist/src/generator/stepTemplate/index.ts.hbs +24 -0
  26. package/dist/src/generator/stepsFlow.d.ts +10 -0
  27. package/dist/src/generator/stepsFlow.js +94 -0
  28. package/dist/src/generator/stepsFlow.js.map +1 -0
  29. package/dist/src/generator/template/.env.example.hbs +3 -0
  30. package/dist/src/generator/template/.eslintignore.hbs +1 -0
  31. package/dist/src/generator/template/.eslintrc.hbs +6 -0
  32. package/dist/src/generator/template/.github/pull_request_template.md.hbs +17 -0
  33. package/dist/src/generator/template/.github/workflows/build.yml.hbs +53 -0
  34. package/dist/src/generator/template/.github/workflows/codeql-analysis.yml.hbs +69 -0
  35. package/dist/src/generator/template/.github/workflows/integration-deployment.yml.hbs +36 -0
  36. package/dist/src/generator/template/.github/workflows/peril.yml.hbs +90 -0
  37. package/dist/src/generator/template/.github/workflows/questions.yml.hbs +40 -0
  38. package/dist/src/generator/template/.gitignore.hbs +8 -0
  39. package/dist/src/generator/template/.node-version.hbs +1 -0
  40. package/dist/src/generator/template/.prettierignore.hbs +5 -0
  41. package/dist/src/generator/template/CHANGELOG.md.hbs +9 -0
  42. package/dist/src/generator/template/CODEOWNERS.hbs +3 -0
  43. package/dist/src/generator/template/Dockerfile.hbs +25 -0
  44. package/dist/src/generator/template/LICENSE.hbs +373 -0
  45. package/dist/src/generator/template/README.md.hbs +114 -0
  46. package/dist/src/generator/template/docs/development.md.hbs +28 -0
  47. package/dist/src/generator/template/docs/jupiterone.md.hbs +1 -0
  48. package/dist/src/generator/template/husky.config.js.hbs +1 -0
  49. package/dist/src/generator/template/jest.config.js.hbs +1 -0
  50. package/dist/src/generator/template/jupiterone/questions/questions.yaml.hbs +16 -0
  51. package/dist/src/generator/template/lint-staged.config.js.hbs +1 -0
  52. package/dist/src/generator/template/package.json.hbs +63 -0
  53. package/dist/src/generator/template/prettier.config.js.hbs +1 -0
  54. package/dist/src/generator/template/scripts/execute.sh.hbs +7 -0
  55. package/dist/src/generator/template/src/client.ts.hbs +23 -0
  56. package/dist/src/generator/template/src/config.ts.hbs +39 -0
  57. package/dist/src/generator/template/src/index.ts.hbs +14 -0
  58. package/dist/src/generator/template/src/steps/constants.ts.hbs +34 -0
  59. package/dist/src/generator/template/src/steps/index.ts.hbs +7 -0
  60. package/dist/src/generator/template/src/validateInvocation.ts.hbs +23 -0
  61. package/dist/src/generator/template/test/README.md.hbs +4 -0
  62. package/dist/src/generator/template/test/config.ts.hbs +30 -0
  63. package/dist/src/generator/template/test/recording.ts.hbs +74 -0
  64. package/dist/src/generator/template/tsconfig.dist.json.hbs +13 -0
  65. package/dist/src/generator/template/tsconfig.json.hbs +7 -0
  66. package/dist/src/generator/util.d.ts +9 -0
  67. package/dist/src/generator/util.js +35 -0
  68. package/dist/src/generator/util.js.map +1 -0
  69. package/dist/src/index.js +2 -1
  70. package/dist/src/index.js.map +1 -1
  71. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  72. package/package.json +12 -6
  73. package/src/commands/generate.ts +28 -0
  74. package/src/commands/index.ts +1 -0
  75. package/src/generator/actions.ts +37 -0
  76. package/src/generator/configFieldsFlow.ts +60 -0
  77. package/src/generator/entitiesFlow.ts +185 -0
  78. package/src/generator/helpers.ts +6 -0
  79. package/src/generator/newIntegration.ts +137 -0
  80. package/src/generator/relationshipsFlow.ts +73 -0
  81. package/src/generator/stepTemplate/index.ts.hbs +24 -0
  82. package/src/generator/stepsFlow.ts +123 -0
  83. package/src/generator/template/.env.example.hbs +3 -0
  84. package/src/generator/template/.eslintignore.hbs +1 -0
  85. package/src/generator/template/.eslintrc.hbs +6 -0
  86. package/src/generator/template/.github/pull_request_template.md.hbs +17 -0
  87. package/src/generator/template/.github/workflows/build.yml.hbs +53 -0
  88. package/src/generator/template/.github/workflows/codeql-analysis.yml.hbs +69 -0
  89. package/src/generator/template/.github/workflows/integration-deployment.yml.hbs +36 -0
  90. package/src/generator/template/.github/workflows/peril.yml.hbs +90 -0
  91. package/src/generator/template/.github/workflows/questions.yml.hbs +40 -0
  92. package/src/generator/template/.gitignore.hbs +8 -0
  93. package/src/generator/template/.node-version.hbs +1 -0
  94. package/src/generator/template/.prettierignore.hbs +5 -0
  95. package/src/generator/template/CHANGELOG.md.hbs +9 -0
  96. package/src/generator/template/CODEOWNERS.hbs +3 -0
  97. package/src/generator/template/Dockerfile.hbs +25 -0
  98. package/src/generator/template/LICENSE.hbs +373 -0
  99. package/src/generator/template/README.md.hbs +114 -0
  100. package/src/generator/template/docs/development.md.hbs +28 -0
  101. package/src/generator/template/docs/jupiterone.md.hbs +1 -0
  102. package/src/generator/template/husky.config.js.hbs +1 -0
  103. package/src/generator/template/jest.config.js.hbs +1 -0
  104. package/src/generator/template/jupiterone/questions/questions.yaml.hbs +16 -0
  105. package/src/generator/template/lint-staged.config.js.hbs +1 -0
  106. package/src/generator/template/package.json.hbs +63 -0
  107. package/src/generator/template/prettier.config.js.hbs +1 -0
  108. package/src/generator/template/scripts/execute.sh.hbs +7 -0
  109. package/src/generator/template/src/client.ts.hbs +23 -0
  110. package/src/generator/template/src/config.ts.hbs +39 -0
  111. package/src/generator/template/src/index.ts.hbs +14 -0
  112. package/src/generator/template/src/steps/constants.ts.hbs +34 -0
  113. package/src/generator/template/src/steps/index.ts.hbs +7 -0
  114. package/src/generator/template/src/validateInvocation.ts.hbs +23 -0
  115. package/src/generator/template/test/README.md.hbs +4 -0
  116. package/src/generator/template/test/config.ts.hbs +30 -0
  117. package/src/generator/template/test/recording.ts.hbs +74 -0
  118. package/src/generator/template/tsconfig.dist.json.hbs +13 -0
  119. package/src/generator/template/tsconfig.json.hbs +7 -0
  120. package/src/generator/util.ts +39 -0
  121. package/src/index.ts +3 -1
  122. package/tsconfig.dist.json +3 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupiterone/integration-sdk-cli",
3
- "version": "9.5.0",
3
+ "version": "9.6.0",
4
4
  "description": "The SDK for developing JupiterOne integrations",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -18,27 +18,33 @@
18
18
  },
19
19
  "scripts": {
20
20
  "prebuild:dist": "rm -rf dist && mkdir dist",
21
- "build:dist": "tsc -p tsconfig.dist.json --declaration",
22
- "prepack": "yarn build:dist"
21
+ "build:dist": "tsc -p tsconfig.dist.json --declaration && yarn copy-files",
22
+ "prepack": "yarn build:dist",
23
+ "copy-files": "cp -r src/generator/template dist/src/generator/template && cp -r src/generator/stepTemplate dist/src/generator/stepTemplate",
24
+ "plop": "plop --plopfile dist/src/generator/newIntegration.js"
23
25
  },
24
26
  "dependencies": {
25
- "@jupiterone/integration-sdk-runtime": "^9.5.0",
27
+ "@jupiterone/data-model": "^0.54.0",
28
+ "@jupiterone/integration-sdk-core": "^9.6.0",
29
+ "@jupiterone/integration-sdk-runtime": "^9.6.0",
26
30
  "chalk": "^4",
27
31
  "commander": "^9.4.0",
28
32
  "fs-extra": "^10.1.0",
29
33
  "globby": "^11.0.0",
34
+ "inquirer-checkbox-plus-prompt": "^1.4.2",
30
35
  "js-yaml": "^4.1.0",
31
36
  "json-diff": "^0.5.4",
32
37
  "lodash": "^4.17.19",
33
38
  "markdown-table": "^2.0.0",
34
39
  "neo4j-driver": "^4.3.3",
40
+ "plop": "^3.1.2",
35
41
  "runtypes": "5.1.0",
36
42
  "upath": "^1.2.0",
37
43
  "url-exists": "^1.0.3",
38
44
  "vis": "^4.21.0-EOL"
39
45
  },
40
46
  "devDependencies": {
41
- "@jupiterone/integration-sdk-private-test-utils": "^9.5.0",
47
+ "@jupiterone/integration-sdk-private-test-utils": "^9.6.0",
42
48
  "@pollyjs/adapter-node-http": "^6.0.5",
43
49
  "@pollyjs/core": "^6.0.5",
44
50
  "@pollyjs/persister-fs": "^6.0.5",
@@ -50,5 +56,5 @@
50
56
  "memfs": "^3.2.0",
51
57
  "neo-forgery": "^2.0.0"
52
58
  },
53
- "gitHead": "8b514643213ef8c00b0f260d6911ffff1783286b"
59
+ "gitHead": "dc14837ec81f9196b852d3ffb4e82e9789097a77"
54
60
  }
@@ -0,0 +1,28 @@
1
+ import path from 'path';
2
+ import { createCommand } from 'commander';
3
+ const dynamicImport = new Function('specifier', 'return import(specifier)');
4
+
5
+ export function generate() {
6
+ return createCommand('generate')
7
+ .description('generate integrations in whole and in part')
8
+ .action(async (cmdOpts) => {
9
+ const Plop = await dynamicImport('plop');
10
+ const configPath = path.resolve(
11
+ path.join(__dirname, '../generator/newIntegration.js'),
12
+ );
13
+ Plop.Plop.prepare(
14
+ {
15
+ cwd: process.cwd(),
16
+ configPath,
17
+ },
18
+ (env) =>
19
+ Plop.Plop.execute(env, (env) => {
20
+ const options = {
21
+ ...env,
22
+ dest: process.cwd(),
23
+ };
24
+ return Plop.run(options, undefined, true);
25
+ }),
26
+ );
27
+ });
28
+ }
@@ -11,3 +11,4 @@ export * from './visualize-dependencies';
11
11
  export * from './generate-integration-graph-schema';
12
12
  export * from './troubleshoot';
13
13
  export * from './generate-ingestion-sources-config';
14
+ export * from './generate';
@@ -0,0 +1,37 @@
1
+ import { spawn } from 'child_process';
2
+
3
+ async function spawnCommand(config, command, args): Promise<string> {
4
+ const spawnOptions = config.verbose
5
+ ? {
6
+ cwd: config.path,
7
+ shell: true,
8
+ }
9
+ : {
10
+ cwd: config.path,
11
+ };
12
+
13
+ return new Promise((resolve, reject) => {
14
+ const cmd = spawn(command, args, spawnOptions);
15
+ cmd.on('close', (code) => {
16
+ if (code === 0) {
17
+ resolve(`${code}`);
18
+ } else {
19
+ reject(`exited with ${code}`);
20
+ }
21
+ });
22
+ });
23
+ }
24
+
25
+ async function yarnInstall(_answers, config, _plop) {
26
+ return spawnCommand(config, 'yarn', ['install']);
27
+ }
28
+
29
+ async function yarnFormat(_answers, config, _plop): Promise<string> {
30
+ return spawnCommand(config, 'yarn', [`format`]);
31
+ }
32
+
33
+ async function yarnLint(_answers, config, _plop) {
34
+ return spawnCommand(config, 'yarn', ['lint']);
35
+ }
36
+
37
+ export { yarnInstall, yarnFormat, yarnLint };
@@ -0,0 +1,60 @@
1
+ import { askRepeatedly } from './util';
2
+
3
+ type FieldType = 'json' | 'string' | 'boolean';
4
+
5
+ async function configFieldsFlow(inquirer) {
6
+ const configFields: { field: string; type: FieldType; mask: boolean }[] = [];
7
+
8
+ const { doPrompt } = await inquirer.prompt({
9
+ type: 'confirm',
10
+ name: 'doPrompt',
11
+ message: 'Do you want to add config fields?',
12
+ });
13
+
14
+ if (!doPrompt) return configFields;
15
+
16
+ await askRepeatedly(inquirer, async () => {
17
+ const configField = await configFieldPrompt(inquirer);
18
+ configFields.push(configField);
19
+ });
20
+
21
+ return configFields;
22
+ }
23
+
24
+ async function configFieldPrompt(inquirer) {
25
+ const { field } = await inquirer.prompt({
26
+ type: 'input',
27
+ name: 'field',
28
+ message: 'The config field name (ex. apiKey)',
29
+ });
30
+
31
+ const { type } = await inquirer.prompt({
32
+ type: 'list',
33
+ name: 'type',
34
+ message: 'The type of the config field',
35
+ choices: [
36
+ {
37
+ name: 'string',
38
+ value: 'string',
39
+ },
40
+ {
41
+ name: 'boolean',
42
+ value: 'boolean',
43
+ },
44
+ {
45
+ name: 'json',
46
+ value: 'json',
47
+ },
48
+ ],
49
+ });
50
+
51
+ const { mask } = await inquirer.prompt({
52
+ name: 'mask',
53
+ type: 'confirm',
54
+ message: 'Should the field be masked?',
55
+ });
56
+
57
+ return { field, type, mask };
58
+ }
59
+
60
+ export { configFieldsFlow };
@@ -0,0 +1,185 @@
1
+ import { askRepeatedly } from './util';
2
+ import { snakeCase } from 'lodash';
3
+
4
+ export type Entity = {
5
+ resourceName: string;
6
+ _type: string;
7
+ _class: string[];
8
+ };
9
+
10
+ async function entitiesFlow(inquirer, vendorName): Promise<Entity[]> {
11
+ const entities: Entity[] = [];
12
+ const { doPrompt } = await inquirer.prompt({
13
+ type: 'confirm',
14
+ name: 'doPrompt',
15
+ message: 'Do you want to declare entities?',
16
+ });
17
+ if (!doPrompt) return entities;
18
+
19
+ await askRepeatedly(inquirer, async () => {
20
+ const entity = await createEntityPrompt(inquirer, vendorName);
21
+ entities.push(entity);
22
+ });
23
+ return entities;
24
+ }
25
+
26
+ async function createEntityPrompt(inquirer, vendorName) {
27
+ const { resourceName } = await inquirer.prompt({
28
+ type: 'input',
29
+ name: 'resourceName',
30
+ message:
31
+ 'What is the name of the entity? ex. User, EC2 Instance, Access Key',
32
+ });
33
+
34
+ const { _class } = await inquirer.prompt({
35
+ type: 'checkbox-plus',
36
+ name: '_class',
37
+ message: 'Which classes should the entity have?',
38
+ searchable: true,
39
+ pageSize: 10,
40
+ validate(input) {
41
+ if (input?.length > 0) {
42
+ return true;
43
+ } else {
44
+ return false;
45
+ }
46
+ },
47
+ source: function (ans, input) {
48
+ return new Promise((resolve) => {
49
+ if (!input) {
50
+ resolve(entityClassesChoices);
51
+ return;
52
+ } else {
53
+ const choices = entityClassesChoices.filter(({ name }) =>
54
+ name.toLowerCase().includes(input.toLowerCase()),
55
+ );
56
+ resolve(choices);
57
+ }
58
+ });
59
+ },
60
+ });
61
+
62
+ const { _type } = await inquirer.prompt({
63
+ Type: 'input',
64
+ name: '_type',
65
+ message: 'What should the _type be?',
66
+ default: `${snakeCase(vendorName)}_${snakeCase(resourceName)}`,
67
+ validate(input) {
68
+ const valid = /^[a-z0-9_]+$/.test(input);
69
+ if (!valid) {
70
+ return '_type must be lowercased, alphanumeric, and use underscores for spacing';
71
+ }
72
+ return valid;
73
+ },
74
+ });
75
+
76
+ return {
77
+ resourceName,
78
+ _type,
79
+ _class,
80
+ };
81
+ }
82
+
83
+ const entityClassesChoices = [
84
+ { name: 'AccessKey' },
85
+ { name: 'AccessPolicy' },
86
+ { name: 'AccessRole' },
87
+ { name: 'Account' },
88
+ { name: 'Alert' },
89
+ { name: 'Application' },
90
+ { name: 'ApplicationEndpoint' },
91
+ { name: 'Assessment' },
92
+ { name: 'Attacker' },
93
+ { name: 'Backup' },
94
+ { name: 'Certificate' },
95
+ { name: 'Channel' },
96
+ { name: 'Cluster' },
97
+ { name: 'CodeCommit' },
98
+ { name: 'CodeDeploy' },
99
+ { name: 'CodeModule' },
100
+ { name: 'CodeRepo' },
101
+ { name: 'CodeReview' },
102
+ { name: 'Configuration' },
103
+ { name: 'Container' },
104
+ { name: 'Control' },
105
+ { name: 'ControlPolicy' },
106
+ { name: 'CryptoKey' },
107
+ { name: 'DataCollection' },
108
+ { name: 'DataObject' },
109
+ { name: 'DataStore' },
110
+ { name: 'Database' },
111
+ { name: 'Deployment' },
112
+ { name: 'Device' },
113
+ { name: 'Directory' },
114
+ { name: 'Disk' },
115
+ { name: 'Document' },
116
+ { name: 'Domain' },
117
+ { name: 'DomainRecord' },
118
+ { name: 'DomainZone' },
119
+ { name: 'Entity' },
120
+ { name: 'Finding' },
121
+ { name: 'Firewall' },
122
+ { name: 'Framework' },
123
+ { name: 'Function' },
124
+ { name: 'Gateway' },
125
+ { name: 'GraphObject' },
126
+ { name: 'Group' },
127
+ { name: 'Host' },
128
+ { name: 'HostAgent' },
129
+ { name: 'Image' },
130
+ { name: 'Incident' },
131
+ { name: 'Internet' },
132
+ { name: 'IpAddress' },
133
+ { name: 'Issue' },
134
+ { name: 'Key' },
135
+ { name: 'Logs' },
136
+ { name: 'Model' },
137
+ { name: 'Module' },
138
+ { name: 'Network' },
139
+ { name: 'NetworkEndpoint' },
140
+ { name: 'NetworkInterface' },
141
+ { name: 'Organization' },
142
+ { name: 'PR' },
143
+ { name: 'PasswordPolicy' },
144
+ { name: 'Person' },
145
+ { name: 'Policy' },
146
+ { name: 'Problem' },
147
+ { name: 'Procedure' },
148
+ { name: 'Process' },
149
+ { name: 'Product' },
150
+ { name: 'Program' },
151
+ { name: 'Project' },
152
+ { name: 'Question' },
153
+ { name: 'Queue' },
154
+ { name: 'Record' },
155
+ { name: 'RecordEntity' },
156
+ { name: 'Repository' },
157
+ { name: 'Requirement' },
158
+ { name: 'Resource' },
159
+ { name: 'Review' },
160
+ { name: 'Risk' },
161
+ { name: 'Root' },
162
+ { name: 'Rule' },
163
+ { name: 'Ruleset' },
164
+ { name: 'Scanner' },
165
+ { name: 'Secret' },
166
+ { name: 'Section' },
167
+ { name: 'Service' },
168
+ { name: 'Site' },
169
+ { name: 'Standard' },
170
+ { name: 'Subscription' },
171
+ { name: 'Task' },
172
+ { name: 'Team' },
173
+ { name: 'ThreatIntel' },
174
+ { name: 'Training' },
175
+ { name: 'User' },
176
+ { name: 'UserGroup' },
177
+ { name: 'Vault' },
178
+ { name: 'Vendor' },
179
+ { name: 'Vulnerability' },
180
+ { name: 'Weakness' },
181
+ { name: 'Workflow' },
182
+ { name: 'Workload' },
183
+ ].map((v) => ({ name: v.name, value: v.name }));
184
+
185
+ export { entitiesFlow };
@@ -0,0 +1,6 @@
1
+ function generateRelationshipName(relationship) {
2
+ const { from, to, _class } = relationship;
3
+ return `${from.resourceName}_${_class}_${to.resourceName}`;
4
+ }
5
+
6
+ export { generateRelationshipName };
@@ -0,0 +1,137 @@
1
+ import { entitiesFlow } from './entitiesFlow';
2
+ import { relationshipsFlow } from './relationshipsFlow';
3
+ import { configFieldsFlow } from './configFieldsFlow';
4
+ import { stepsFlow } from './stepsFlow';
5
+ import { generateRelationshipName } from './helpers';
6
+ import { generateRelationshipType } from '@jupiterone/integration-sdk-core';
7
+ import { yarnFormat, yarnInstall, yarnLint } from './actions';
8
+ import { kebabCase } from 'lodash';
9
+ import checkboxPlus from 'inquirer-checkbox-plus-prompt';
10
+ import path from 'path';
11
+
12
+ import { NodePlopAPI } from 'plop';
13
+ import { Relationship } from './relationshipsFlow';
14
+ import { Step } from './stepsFlow';
15
+
16
+ function newIntegration(plop: NodePlopAPI) {
17
+ plop.setHelper('generateRelationshipType', function (this: any) {
18
+ return generateRelationshipType(this._class, this.from, this.to);
19
+ });
20
+
21
+ plop.setHelper('generateRelationshipName', generateRelationshipName);
22
+ plop.setActionType('yarnFormat', yarnFormat);
23
+ plop.setActionType('yarnInstall', yarnInstall);
24
+ plop.setActionType('yarnLint', yarnLint);
25
+ plop.setPrompt('checkbox-plus', checkboxPlus);
26
+
27
+ plop.setGenerator('new:integration', {
28
+ description: 'Create a new integration',
29
+ prompts: async function (inquirer) {
30
+ const { vendorName } = await inquirer.prompt({
31
+ type: 'input',
32
+ name: 'vendorName',
33
+ message:
34
+ 'The integration vendor name (ex. AWS, Google Workspace, CrowdStrike)',
35
+ validate(input) {
36
+ if (!input) {
37
+ return 'Cannot be an empty string';
38
+ }
39
+ return true;
40
+ },
41
+ });
42
+
43
+ const { packageName } = await inquirer.prompt({
44
+ type: 'input',
45
+ name: 'packageName',
46
+ default: `@jupiterone/graph-${kebabCase(vendorName)}`,
47
+ message: 'The npm package name',
48
+ validate(input) {
49
+ if (!input) {
50
+ return 'Cannot be an empty string';
51
+ }
52
+ return true;
53
+ },
54
+ });
55
+
56
+ const { packageDescription } = await inquirer.prompt({
57
+ type: 'input',
58
+ name: 'packageDescription',
59
+ message: 'A description for package',
60
+ default: `An integration and graph conversion project for ${vendorName}`,
61
+ validate(input) {
62
+ if (!input) {
63
+ return 'Cannot be an empty string';
64
+ }
65
+ return true;
66
+ },
67
+ });
68
+
69
+ const configFields = await configFieldsFlow(inquirer);
70
+ const entities = await entitiesFlow(inquirer, vendorName);
71
+ let relationships: Relationship[] = [];
72
+ let steps: Step[] = [];
73
+ if (entities.length) {
74
+ relationships = await relationshipsFlow(inquirer, entities);
75
+ steps = await stepsFlow(inquirer, entities, relationships);
76
+ }
77
+
78
+ return {
79
+ vendorName,
80
+ packageName,
81
+ packageDescription,
82
+ configFields,
83
+ entities,
84
+ relationships,
85
+ steps,
86
+ };
87
+ },
88
+ actions: function (data) {
89
+ if (!data) {
90
+ return [];
91
+ }
92
+
93
+ const actions: any[] = [];
94
+ actions.push({
95
+ type: 'addMany',
96
+ destination: '.',
97
+ base: path.join(__dirname, '/template'),
98
+ templateFiles: path.join(__dirname + '/template/**'),
99
+ globOptions: { dot: true },
100
+ force: true,
101
+ data,
102
+ });
103
+
104
+ for (const step of data.steps) {
105
+ actions.push({
106
+ type: 'add',
107
+ path: path.normalize(`src/steps/${kebabCase(step.name)}/index.ts`),
108
+ templateFile: path.join(__dirname, 'stepTemplate/index.ts.hbs'),
109
+ data: step,
110
+ force: true,
111
+ });
112
+ }
113
+
114
+ actions.push({
115
+ type: 'yarnInstall',
116
+ path: '.',
117
+ verbose: true,
118
+ });
119
+
120
+ actions.push({
121
+ type: 'yarnFormat',
122
+ path: '.',
123
+ verbose: true,
124
+ });
125
+
126
+ actions.push({
127
+ type: 'yarnLint',
128
+ path: '.',
129
+ verbose: true,
130
+ });
131
+
132
+ return actions;
133
+ },
134
+ });
135
+ }
136
+
137
+ module.exports = newIntegration;
@@ -0,0 +1,73 @@
1
+ import { askRepeatedly, generateChoicesFromEntities } from './util';
2
+ import { RelationshipClass } from '@jupiterone/data-model';
3
+
4
+ export type Relationship = {
5
+ from: any;
6
+ to: any;
7
+ _class: any;
8
+ };
9
+
10
+ async function relationshipsFlow(inquirer, entities): Promise<Relationship[]> {
11
+ const relationships: Relationship[] = [];
12
+ const { doPrompt } = await inquirer.prompt({
13
+ type: 'confirm',
14
+ name: 'doPrompt',
15
+ message: 'Do you want to add relationships?',
16
+ });
17
+ if (!doPrompt) return relationships;
18
+
19
+ const entityChoices = generateChoicesFromEntities(entities);
20
+ await askRepeatedly(inquirer, async () => {
21
+ const relationship = await createRelationship(inquirer, entityChoices);
22
+ relationships.push(relationship);
23
+ });
24
+ return relationships;
25
+ }
26
+
27
+ async function createRelationship(inquirer, entityChoices) {
28
+ const { from } = await inquirer.prompt({
29
+ type: 'list',
30
+ name: 'from',
31
+ message: 'What entity should the relationship be from?',
32
+ choices: entityChoices,
33
+ pageSize: entityChoices.length,
34
+ });
35
+
36
+ const { to } = await inquirer.prompt({
37
+ type: 'list',
38
+ name: 'to',
39
+ message: 'What entity should the relationship be to?',
40
+ choices: entityChoices,
41
+ pageSize: entityChoices.length,
42
+ });
43
+
44
+ const { _class } = await inquirer.prompt({
45
+ type: 'list',
46
+ name: '_class',
47
+ message: 'What should the _class be?',
48
+ choices: relationshipClassChoices(),
49
+ pageSize: 10,
50
+ });
51
+
52
+ return { from, to, _class };
53
+ }
54
+
55
+ function relationshipClassChoices() {
56
+ const choices: { name: string; value: string }[] = [];
57
+ for (const [name, value] of Object.entries(RelationshipClass)) {
58
+ choices.push({
59
+ name,
60
+ value,
61
+ });
62
+ }
63
+ choices.sort((a, b) => {
64
+ if (a.name < b.name) {
65
+ return -1;
66
+ } else {
67
+ return 1;
68
+ }
69
+ });
70
+ return choices;
71
+ }
72
+
73
+ export { relationshipsFlow };
@@ -0,0 +1,24 @@
1
+ import {
2
+ IntegrationStep,
3
+ IntegrationStepExecutionContext,
4
+ } from '@jupiterone/integration-sdk-core';
5
+
6
+ import { IntegrationConfig } from '../../config';
7
+ import { Steps{{#if entities}}, Entities{{/if}}{{#if relationships}}, Relationships{{/if}} } from '../constants';
8
+
9
+ export const {{camelCase name}}Steps: IntegrationStep<IntegrationConfig>[] = [
10
+ {
11
+ id: Steps.{{constantCase name}},
12
+ name: '{{titleCase name}}',
13
+ entities: [{{#each entities}}Entities.{{constantCase resourceName}}{{#unless @last}},{{/unless}}{{/each}}],
14
+ relationships: [{{#each relationships}}Relationships.{{constantCase (generateRelationshipName this)}}{{#unless @last}},{{/unless}}{{/each}}],
15
+ dependsOn: [],
16
+ executionHandler: {{camelCase name}},
17
+ },
18
+ ];
19
+
20
+ export async function {{camelCase name}}({
21
+ jobState,
22
+ }: IntegrationStepExecutionContext<IntegrationConfig>) {
23
+ // TODO
24
+ }