@jupiterone/integration-sdk-cli 9.4.1 → 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 (127) hide show
  1. package/dist/src/commands/generate-ingestion-sources-config.d.ts +2 -2
  2. package/dist/src/commands/generate-ingestion-sources-config.js +4 -5
  3. package/dist/src/commands/generate-ingestion-sources-config.js.map +1 -1
  4. package/dist/src/commands/generate.d.ts +1 -0
  5. package/dist/src/commands/generate.js +29 -0
  6. package/dist/src/commands/generate.js.map +1 -0
  7. package/dist/src/commands/index.d.ts +1 -0
  8. package/dist/src/commands/index.js +1 -0
  9. package/dist/src/commands/index.js.map +1 -1
  10. package/dist/src/generator/actions.d.ts +4 -0
  11. package/dist/src/generator/actions.js +38 -0
  12. package/dist/src/generator/actions.js.map +1 -0
  13. package/dist/src/generator/configFieldsFlow.d.ts +7 -0
  14. package/dist/src/generator/configFieldsFlow.js +53 -0
  15. package/dist/src/generator/configFieldsFlow.js.map +1 -0
  16. package/dist/src/generator/entitiesFlow.d.ts +7 -0
  17. package/dist/src/generator/entitiesFlow.js +175 -0
  18. package/dist/src/generator/entitiesFlow.js.map +1 -0
  19. package/dist/src/generator/helpers.d.ts +2 -0
  20. package/dist/src/generator/helpers.js +9 -0
  21. package/dist/src/generator/helpers.js.map +1 -0
  22. package/dist/src/generator/newIntegration.d.ts +1 -0
  23. package/dist/src/generator/newIntegration.js +124 -0
  24. package/dist/src/generator/newIntegration.js.map +1 -0
  25. package/dist/src/generator/relationshipsFlow.d.ts +7 -0
  26. package/dist/src/generator/relationshipsFlow.js +65 -0
  27. package/dist/src/generator/relationshipsFlow.js.map +1 -0
  28. package/dist/src/generator/stepTemplate/index.ts.hbs +24 -0
  29. package/dist/src/generator/stepsFlow.d.ts +10 -0
  30. package/dist/src/generator/stepsFlow.js +94 -0
  31. package/dist/src/generator/stepsFlow.js.map +1 -0
  32. package/dist/src/generator/template/.env.example.hbs +3 -0
  33. package/dist/src/generator/template/.eslintignore.hbs +1 -0
  34. package/dist/src/generator/template/.eslintrc.hbs +6 -0
  35. package/dist/src/generator/template/.github/pull_request_template.md.hbs +17 -0
  36. package/dist/src/generator/template/.github/workflows/build.yml.hbs +53 -0
  37. package/dist/src/generator/template/.github/workflows/codeql-analysis.yml.hbs +69 -0
  38. package/dist/src/generator/template/.github/workflows/integration-deployment.yml.hbs +36 -0
  39. package/dist/src/generator/template/.github/workflows/peril.yml.hbs +90 -0
  40. package/dist/src/generator/template/.github/workflows/questions.yml.hbs +40 -0
  41. package/dist/src/generator/template/.gitignore.hbs +8 -0
  42. package/dist/src/generator/template/.node-version.hbs +1 -0
  43. package/dist/src/generator/template/.prettierignore.hbs +5 -0
  44. package/dist/src/generator/template/CHANGELOG.md.hbs +9 -0
  45. package/dist/src/generator/template/CODEOWNERS.hbs +3 -0
  46. package/dist/src/generator/template/Dockerfile.hbs +25 -0
  47. package/dist/src/generator/template/LICENSE.hbs +373 -0
  48. package/dist/src/generator/template/README.md.hbs +114 -0
  49. package/dist/src/generator/template/docs/development.md.hbs +28 -0
  50. package/dist/src/generator/template/docs/jupiterone.md.hbs +1 -0
  51. package/dist/src/generator/template/husky.config.js.hbs +1 -0
  52. package/dist/src/generator/template/jest.config.js.hbs +1 -0
  53. package/dist/src/generator/template/jupiterone/questions/questions.yaml.hbs +16 -0
  54. package/dist/src/generator/template/lint-staged.config.js.hbs +1 -0
  55. package/dist/src/generator/template/package.json.hbs +63 -0
  56. package/dist/src/generator/template/prettier.config.js.hbs +1 -0
  57. package/dist/src/generator/template/scripts/execute.sh.hbs +7 -0
  58. package/dist/src/generator/template/src/client.ts.hbs +23 -0
  59. package/dist/src/generator/template/src/config.ts.hbs +39 -0
  60. package/dist/src/generator/template/src/index.ts.hbs +14 -0
  61. package/dist/src/generator/template/src/steps/constants.ts.hbs +34 -0
  62. package/dist/src/generator/template/src/steps/index.ts.hbs +7 -0
  63. package/dist/src/generator/template/src/validateInvocation.ts.hbs +23 -0
  64. package/dist/src/generator/template/test/README.md.hbs +4 -0
  65. package/dist/src/generator/template/test/config.ts.hbs +30 -0
  66. package/dist/src/generator/template/test/recording.ts.hbs +74 -0
  67. package/dist/src/generator/template/tsconfig.dist.json.hbs +13 -0
  68. package/dist/src/generator/template/tsconfig.json.hbs +7 -0
  69. package/dist/src/generator/util.d.ts +9 -0
  70. package/dist/src/generator/util.js +35 -0
  71. package/dist/src/generator/util.js.map +1 -0
  72. package/dist/src/index.js +2 -1
  73. package/dist/src/index.js.map +1 -1
  74. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  75. package/package.json +12 -6
  76. package/src/commands/generate-ingestion-sources-config.test.ts +32 -24
  77. package/src/commands/generate-ingestion-sources-config.ts +12 -10
  78. package/src/commands/generate.ts +28 -0
  79. package/src/commands/index.ts +1 -0
  80. package/src/generator/actions.ts +37 -0
  81. package/src/generator/configFieldsFlow.ts +60 -0
  82. package/src/generator/entitiesFlow.ts +185 -0
  83. package/src/generator/helpers.ts +6 -0
  84. package/src/generator/newIntegration.ts +137 -0
  85. package/src/generator/relationshipsFlow.ts +73 -0
  86. package/src/generator/stepTemplate/index.ts.hbs +24 -0
  87. package/src/generator/stepsFlow.ts +123 -0
  88. package/src/generator/template/.env.example.hbs +3 -0
  89. package/src/generator/template/.eslintignore.hbs +1 -0
  90. package/src/generator/template/.eslintrc.hbs +6 -0
  91. package/src/generator/template/.github/pull_request_template.md.hbs +17 -0
  92. package/src/generator/template/.github/workflows/build.yml.hbs +53 -0
  93. package/src/generator/template/.github/workflows/codeql-analysis.yml.hbs +69 -0
  94. package/src/generator/template/.github/workflows/integration-deployment.yml.hbs +36 -0
  95. package/src/generator/template/.github/workflows/peril.yml.hbs +90 -0
  96. package/src/generator/template/.github/workflows/questions.yml.hbs +40 -0
  97. package/src/generator/template/.gitignore.hbs +8 -0
  98. package/src/generator/template/.node-version.hbs +1 -0
  99. package/src/generator/template/.prettierignore.hbs +5 -0
  100. package/src/generator/template/CHANGELOG.md.hbs +9 -0
  101. package/src/generator/template/CODEOWNERS.hbs +3 -0
  102. package/src/generator/template/Dockerfile.hbs +25 -0
  103. package/src/generator/template/LICENSE.hbs +373 -0
  104. package/src/generator/template/README.md.hbs +114 -0
  105. package/src/generator/template/docs/development.md.hbs +28 -0
  106. package/src/generator/template/docs/jupiterone.md.hbs +1 -0
  107. package/src/generator/template/husky.config.js.hbs +1 -0
  108. package/src/generator/template/jest.config.js.hbs +1 -0
  109. package/src/generator/template/jupiterone/questions/questions.yaml.hbs +16 -0
  110. package/src/generator/template/lint-staged.config.js.hbs +1 -0
  111. package/src/generator/template/package.json.hbs +63 -0
  112. package/src/generator/template/prettier.config.js.hbs +1 -0
  113. package/src/generator/template/scripts/execute.sh.hbs +7 -0
  114. package/src/generator/template/src/client.ts.hbs +23 -0
  115. package/src/generator/template/src/config.ts.hbs +39 -0
  116. package/src/generator/template/src/index.ts.hbs +14 -0
  117. package/src/generator/template/src/steps/constants.ts.hbs +34 -0
  118. package/src/generator/template/src/steps/index.ts.hbs +7 -0
  119. package/src/generator/template/src/validateInvocation.ts.hbs +23 -0
  120. package/src/generator/template/test/README.md.hbs +4 -0
  121. package/src/generator/template/test/config.ts.hbs +30 -0
  122. package/src/generator/template/test/recording.ts.hbs +74 -0
  123. package/src/generator/template/tsconfig.dist.json.hbs +13 -0
  124. package/src/generator/template/tsconfig.json.hbs +7 -0
  125. package/src/generator/util.ts +39 -0
  126. package/src/index.ts +3 -1
  127. 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.4.1",
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.4.1",
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.4.1",
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": "b9e06bbbd2b454182799a40083c9873dd4b76ade"
59
+ "gitHead": "dc14837ec81f9196b852d3ffb4e82e9789097a77"
54
60
  }
@@ -63,6 +63,33 @@ describe('#generateIngestionSourcesConfig', () => {
63
63
  });
64
64
 
65
65
  it('should return the ingestionConfig with childIngestionSources', () => {
66
+ const stepFetchVulnerabilityAlerts = {
67
+ id: 'fetch-vulnerability-alerts',
68
+ name: 'Fetch Vulnerability Alerts',
69
+ entities: [
70
+ {
71
+ resourceName: 'GitHub Vulnerability Alerts',
72
+ _type: 'github_finding',
73
+ _class: ['Finding'],
74
+ },
75
+ ],
76
+ relationships: [],
77
+ dependsOn: ['fetch-repos'],
78
+ ingestionSourceId: INGESTION_SOURCE_IDS.FINDING_ALERTS,
79
+ };
80
+ const stepFetchIssues = {
81
+ id: 'fetch-issues',
82
+ name: 'Fetch Issues',
83
+ entities: [
84
+ {
85
+ resourceName: 'GitHub Issue',
86
+ _type: 'github_issue',
87
+ _class: ['Issue'],
88
+ },
89
+ ],
90
+ relationships: [],
91
+ dependsOn: ['fetch-repos', 'fetch-users', 'fetch-collaborators'],
92
+ };
66
93
  const integrationSteps: IntegrationStep<IntegrationInstanceConfig>[] = [
67
94
  {
68
95
  id: 'fetch-repos',
@@ -80,32 +107,11 @@ describe('#generateIngestionSourcesConfig', () => {
80
107
  executionHandler: jest.fn(),
81
108
  },
82
109
  {
83
- id: 'fetch-vulnerability-alerts',
84
- name: 'Fetch Vulnerability Alerts',
85
- entities: [
86
- {
87
- resourceName: 'GitHub Vulnerability Alerts',
88
- _type: 'github_finding',
89
- _class: ['Finding'],
90
- },
91
- ],
92
- relationships: [],
93
- dependsOn: ['fetch-repos'],
94
- ingestionSourceId: INGESTION_SOURCE_IDS.FINDING_ALERTS,
110
+ ...stepFetchVulnerabilityAlerts,
95
111
  executionHandler: jest.fn(),
96
112
  },
97
113
  {
98
- id: 'fetch-issues',
99
- name: 'Fetch Issues',
100
- entities: [
101
- {
102
- resourceName: 'GitHub Issue',
103
- _type: 'github_issue',
104
- _class: ['Issue'],
105
- },
106
- ],
107
- relationships: [],
108
- dependsOn: ['fetch-repos', 'fetch-users', 'fetch-collaborators'],
114
+ ...stepFetchIssues,
109
115
  executionHandler: jest.fn(),
110
116
  },
111
117
  {
@@ -135,7 +141,9 @@ describe('#generateIngestionSourcesConfig', () => {
135
141
  expect(
136
142
  ingestionSourcesConfig[INGESTION_SOURCE_IDS.FETCH_REPOS]
137
143
  .childIngestionSources,
138
- ).toEqual(['fetch-vulnerability-alerts', 'fetch-issues']);
144
+ ).toEqual(
145
+ expect.arrayContaining([stepFetchVulnerabilityAlerts, stepFetchIssues]),
146
+ );
139
147
  // For FINDING_ALERTS the ingestionConfig keep exactly the same
140
148
  expect(
141
149
  ingestionSourcesConfig[INGESTION_SOURCE_IDS.FINDING_ALERTS],
@@ -4,6 +4,7 @@ import {
4
4
  IntegrationSourceId,
5
5
  Step,
6
6
  StepExecutionContext,
7
+ StepMetadata,
7
8
  } from '@jupiterone/integration-sdk-core';
8
9
  import { createCommand } from 'commander';
9
10
  import { loadConfigFromTarget } from '../config';
@@ -59,7 +60,7 @@ export function generateIngestionSourcesConfigCommand() {
59
60
 
60
61
  export type EnhancedIntegrationIngestionConfigFieldMap = Record<
61
62
  IntegrationSourceId,
62
- IntegrationIngestionConfigField & { childIngestionSources?: string[] }
63
+ IntegrationIngestionConfigField & { childIngestionSources?: StepMetadata[] }
63
64
  >;
64
65
 
65
66
  /**
@@ -91,18 +92,19 @@ export function generateIngestionSourcesConfig<
91
92
  // Skip iteration if there are no steps pointing to the current ingestionSourceId
92
93
  return;
93
94
  }
94
- // Get the stepIds that have any dependencies on the matched step ids
95
- const childIngestionSources = integrationSteps
96
- .filter((step) =>
97
- step.dependsOn?.some((value) =>
98
- matchedIntegrationStepIds.includes(value),
99
- ),
100
- )
101
- .map(({ id }) => id);
95
+ // Get the dependent steps for the given matchedIntegrationStepIds
96
+ const childIngestionSources = integrationSteps.filter((step) =>
97
+ step.dependsOn?.some((value) =>
98
+ matchedIntegrationStepIds.includes(value),
99
+ ),
100
+ );
102
101
  // Generate ingestionConfig with the childIngestionSources
103
102
  newIngestionConfig[key] = {
104
103
  ...ingestionConfig[key],
105
- childIngestionSources,
104
+ // Drop execution handler from returned object
105
+ childIngestionSources: childIngestionSources.map(
106
+ ({ executionHandler, ...keepAttrs }) => keepAttrs,
107
+ ),
106
108
  };
107
109
  } else {
108
110
  log.warn(`The key ${key} does not exist in the ingestionConfig`);
@@ -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;