@jupiterone/integration-sdk-cli 12.7.1 → 12.8.3

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 (174) hide show
  1. package/dist/src/bocchi/actions/steps.d.ts +4 -0
  2. package/dist/src/bocchi/actions/steps.js +20 -0
  3. package/dist/src/bocchi/actions/steps.js.map +1 -0
  4. package/dist/src/bocchi/bocchi.d.ts +1 -0
  5. package/dist/src/bocchi/bocchi.js +260 -0
  6. package/dist/src/bocchi/bocchi.js.map +1 -0
  7. package/dist/src/bocchi/templates/partials/directRelationships.hbs +26 -0
  8. package/dist/src/bocchi/templates/partials/mappedRelationships.hbs +36 -0
  9. package/dist/src/bocchi/templates/partials/stepMap.hbs +47 -0
  10. package/dist/src/bocchi/templates/steps/child-singleton.ts.hbs +47 -0
  11. package/dist/src/bocchi/templates/steps/fetch-child-entities.ts.hbs +58 -0
  12. package/dist/src/bocchi/templates/steps/fetch-entities.ts.hbs +47 -0
  13. package/dist/src/bocchi/templates/steps/index.test.ts.hbs +41 -0
  14. package/dist/src/bocchi/templates/steps/singleton.ts.hbs +45 -0
  15. package/dist/src/bocchi/templates/steps/spec.ts.hbs +73 -0
  16. package/dist/src/bocchi/templates/top-level/.env.example.hbs +3 -0
  17. package/dist/src/bocchi/templates/top-level/.eslintignore.hbs +1 -0
  18. package/dist/src/bocchi/templates/top-level/.eslintrc.hbs +6 -0
  19. package/dist/src/bocchi/templates/top-level/.github/workflows/build.yml.hbs +29 -0
  20. package/dist/src/bocchi/templates/top-level/.github/workflows/questions.yml.hbs +40 -0
  21. package/dist/src/bocchi/templates/top-level/.gitignore.hbs +8 -0
  22. package/dist/src/bocchi/templates/top-level/.node-version.hbs +1 -0
  23. package/dist/src/bocchi/templates/top-level/.prettierignore.hbs +6 -0
  24. package/dist/src/bocchi/templates/top-level/CHANGELOG.md.hbs +9 -0
  25. package/dist/src/bocchi/templates/top-level/CODEOWNERS.hbs +3 -0
  26. package/dist/src/bocchi/templates/top-level/Dockerfile.hbs +25 -0
  27. package/dist/src/bocchi/templates/top-level/LICENSE.hbs +373 -0
  28. package/dist/src/bocchi/templates/top-level/README.md.hbs +114 -0
  29. package/dist/src/bocchi/templates/top-level/docs/development.md.hbs +28 -0
  30. package/dist/src/bocchi/templates/top-level/docs/jupiterone.md.hbs +1 -0
  31. package/dist/src/bocchi/templates/top-level/docs/spec/index.ts.hbs +14 -0
  32. package/dist/src/bocchi/templates/top-level/husky.config.js.hbs +1 -0
  33. package/dist/src/bocchi/templates/top-level/jest.config.js.hbs +1 -0
  34. package/dist/src/bocchi/templates/top-level/jupiterone/questions/questions.yaml.hbs +16 -0
  35. package/dist/src/bocchi/templates/top-level/lint-staged.config.js.hbs +1 -0
  36. package/dist/src/bocchi/templates/top-level/package.json.hbs +49 -0
  37. package/dist/src/bocchi/templates/top-level/prettier.config.js.hbs +1 -0
  38. package/dist/src/bocchi/templates/top-level/src/client.ts.hbs +116 -0
  39. package/dist/src/bocchi/templates/top-level/src/config.ts.hbs +41 -0
  40. package/dist/src/bocchi/templates/top-level/src/index.test.ts.hbs +6 -0
  41. package/dist/src/bocchi/templates/top-level/src/index.ts.hbs +14 -0
  42. package/dist/src/bocchi/templates/top-level/src/steps/constants.ts.hbs +60 -0
  43. package/dist/src/bocchi/templates/top-level/src/steps/converters.ts.hbs +37 -0
  44. package/dist/src/bocchi/templates/top-level/src/steps/index.ts.hbs +13 -0
  45. package/dist/src/bocchi/templates/top-level/src/steps/types.ts.hbs +6 -0
  46. package/dist/src/bocchi/templates/top-level/test/README.md.hbs +5 -0
  47. package/dist/src/bocchi/templates/top-level/test/config.ts.hbs +30 -0
  48. package/dist/src/bocchi/templates/top-level/test/recording.ts.hbs +74 -0
  49. package/dist/src/bocchi/templates/top-level/tsconfig.dist.json.hbs +13 -0
  50. package/dist/src/bocchi/templates/top-level/tsconfig.json.hbs +7 -0
  51. package/dist/src/bocchi/utils/types.d.ts +98 -0
  52. package/dist/src/bocchi/utils/types.js +10 -0
  53. package/dist/src/bocchi/utils/types.js.map +1 -0
  54. package/dist/src/commands/bocchi.d.ts +1 -0
  55. package/dist/src/commands/bocchi.js +29 -0
  56. package/dist/src/commands/bocchi.js.map +1 -0
  57. package/dist/src/commands/collect.js.map +1 -1
  58. package/dist/src/commands/diff.js.map +1 -1
  59. package/dist/src/commands/document.js.map +1 -1
  60. package/dist/src/commands/generate-ingestion-sources-config.js.map +1 -1
  61. package/dist/src/commands/generate-integration-graph-schema.d.ts +2 -1
  62. package/dist/src/commands/generate-integration-graph-schema.js +1 -0
  63. package/dist/src/commands/generate-integration-graph-schema.js.map +1 -1
  64. package/dist/src/commands/generate.js +1 -1
  65. package/dist/src/commands/generate.js.map +1 -1
  66. package/dist/src/commands/index.d.ts +1 -0
  67. package/dist/src/commands/index.js +1 -0
  68. package/dist/src/commands/index.js.map +1 -1
  69. package/dist/src/commands/options.js.map +1 -1
  70. package/dist/src/commands/run.js.map +1 -1
  71. package/dist/src/commands/validate-question-file.js.map +1 -1
  72. package/dist/src/commands/visualize-types.js.map +1 -1
  73. package/dist/src/config.js.map +1 -1
  74. package/dist/src/generator/actions.d.ts +5 -1
  75. package/dist/src/generator/actions.js +19 -1
  76. package/dist/src/generator/actions.js.map +1 -1
  77. package/dist/src/generator/entitiesFlow.js.map +1 -1
  78. package/dist/src/generator/newIntegration.js +17 -3
  79. package/dist/src/generator/newIntegration.js.map +1 -1
  80. package/dist/src/generator/relationshipsFlow.js.map +1 -1
  81. package/dist/src/generator/stepsFlow.js.map +1 -1
  82. package/dist/src/generator/template/.github/workflows/build.yml.hbs +18 -0
  83. package/dist/src/generator/template/.github/workflows/questions.yml.hbs +12 -1
  84. package/dist/src/generator/template/Dockerfile.hbs +16 -0
  85. package/dist/src/generator/template/README.md.hbs +19 -5
  86. package/dist/src/generator/template/package.json.hbs +8 -2
  87. package/dist/src/generator/util.js.map +1 -1
  88. package/dist/src/index.js +2 -1
  89. package/dist/src/index.js.map +1 -1
  90. package/dist/src/log.js.map +1 -1
  91. package/dist/src/neo4j/neo4jGraphStore.js.map +1 -1
  92. package/dist/src/neo4j/neo4jUtilities.js.map +1 -1
  93. package/dist/src/neo4j/uploadToNeo4j.js.map +1 -1
  94. package/dist/src/neo4j/wipeNeo4j.js.map +1 -1
  95. package/dist/src/questions/managedQuestionFileValidator.js.map +1 -1
  96. package/dist/src/services/queryLanguage.js.map +1 -1
  97. package/dist/src/troubleshoot/utils.js.map +1 -1
  98. package/dist/src/utils/generateVisHTML.js.map +1 -1
  99. package/dist/src/utils/getSortedJupiterOneTypes.js.map +1 -1
  100. package/dist/src/visualization/createMappedRelationshipNodesAndEdges.js.map +1 -1
  101. package/dist/src/visualization/generateDependencyVisualization.js.map +1 -1
  102. package/dist/src/visualization/generateVisualization.js.map +1 -1
  103. package/dist/src/visualization/retrieveIntegrationData.js.map +1 -1
  104. package/dist/src/visualization/utils.js.map +1 -1
  105. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  106. package/package.json +11 -9
  107. package/src/bocchi/README.md +95 -0
  108. package/src/bocchi/actions/steps.ts +17 -0
  109. package/src/bocchi/bocchi.ts +311 -0
  110. package/src/bocchi/docs/template/README.md +140 -0
  111. package/src/bocchi/docs/template/authentication.md +100 -0
  112. package/src/bocchi/docs/template/examples/example.json +127 -0
  113. package/src/bocchi/docs/template/examples/signalSciences.json +128 -0
  114. package/src/bocchi/docs/template/steps.md +656 -0
  115. package/src/bocchi/templates/partials/directRelationships.hbs +26 -0
  116. package/src/bocchi/templates/partials/mappedRelationships.hbs +36 -0
  117. package/src/bocchi/templates/partials/stepMap.hbs +47 -0
  118. package/src/bocchi/templates/steps/child-singleton.ts.hbs +47 -0
  119. package/src/bocchi/templates/steps/fetch-child-entities.ts.hbs +58 -0
  120. package/src/bocchi/templates/steps/fetch-entities.ts.hbs +47 -0
  121. package/src/bocchi/templates/steps/index.test.ts.hbs +41 -0
  122. package/src/bocchi/templates/steps/singleton.ts.hbs +45 -0
  123. package/src/bocchi/templates/steps/spec.ts.hbs +73 -0
  124. package/src/bocchi/templates/top-level/.env.example.hbs +3 -0
  125. package/src/bocchi/templates/top-level/.eslintignore.hbs +1 -0
  126. package/src/bocchi/templates/top-level/.eslintrc.hbs +6 -0
  127. package/src/bocchi/templates/top-level/.github/workflows/build.yml.hbs +29 -0
  128. package/src/bocchi/templates/top-level/.github/workflows/questions.yml.hbs +40 -0
  129. package/src/bocchi/templates/top-level/.gitignore.hbs +8 -0
  130. package/src/bocchi/templates/top-level/.node-version.hbs +1 -0
  131. package/src/bocchi/templates/top-level/.prettierignore.hbs +6 -0
  132. package/src/bocchi/templates/top-level/CHANGELOG.md.hbs +9 -0
  133. package/src/bocchi/templates/top-level/CODEOWNERS.hbs +3 -0
  134. package/src/bocchi/templates/top-level/Dockerfile.hbs +25 -0
  135. package/src/bocchi/templates/top-level/LICENSE.hbs +373 -0
  136. package/src/bocchi/templates/top-level/README.md.hbs +114 -0
  137. package/src/bocchi/templates/top-level/docs/development.md.hbs +28 -0
  138. package/src/bocchi/templates/top-level/docs/jupiterone.md.hbs +1 -0
  139. package/src/bocchi/templates/top-level/docs/spec/index.ts.hbs +14 -0
  140. package/src/bocchi/templates/top-level/husky.config.js.hbs +1 -0
  141. package/src/bocchi/templates/top-level/jest.config.js.hbs +1 -0
  142. package/src/bocchi/templates/top-level/jupiterone/questions/questions.yaml.hbs +16 -0
  143. package/src/bocchi/templates/top-level/lint-staged.config.js.hbs +1 -0
  144. package/src/bocchi/templates/top-level/package.json.hbs +49 -0
  145. package/src/bocchi/templates/top-level/prettier.config.js.hbs +1 -0
  146. package/src/bocchi/templates/top-level/src/client.ts.hbs +116 -0
  147. package/src/bocchi/templates/top-level/src/config.ts.hbs +41 -0
  148. package/src/bocchi/templates/top-level/src/index.test.ts.hbs +6 -0
  149. package/src/bocchi/templates/top-level/src/index.ts.hbs +14 -0
  150. package/src/bocchi/templates/top-level/src/steps/constants.ts.hbs +60 -0
  151. package/src/bocchi/templates/top-level/src/steps/converters.ts.hbs +37 -0
  152. package/src/bocchi/templates/top-level/src/steps/index.ts.hbs +13 -0
  153. package/src/bocchi/templates/top-level/src/steps/types.ts.hbs +6 -0
  154. package/src/bocchi/templates/top-level/test/README.md.hbs +5 -0
  155. package/src/bocchi/templates/top-level/test/config.ts.hbs +30 -0
  156. package/src/bocchi/templates/top-level/test/recording.ts.hbs +74 -0
  157. package/src/bocchi/templates/top-level/tsconfig.dist.json.hbs +13 -0
  158. package/src/bocchi/templates/top-level/tsconfig.json.hbs +7 -0
  159. package/src/bocchi/utils/types.ts +106 -0
  160. package/src/commands/bocchi.ts +28 -0
  161. package/src/commands/generate-ingestion-sources-config.ts +4 -5
  162. package/src/commands/generate-integration-graph-schema.test.ts +56 -0
  163. package/src/commands/generate-integration-graph-schema.ts +3 -0
  164. package/src/commands/generate.ts +1 -1
  165. package/src/commands/index.ts +1 -0
  166. package/src/generator/actions.ts +28 -1
  167. package/src/generator/newIntegration.ts +28 -4
  168. package/src/generator/template/.github/workflows/build.yml.hbs +18 -0
  169. package/src/generator/template/.github/workflows/questions.yml.hbs +12 -1
  170. package/src/generator/template/Dockerfile.hbs +16 -0
  171. package/src/generator/template/README.md.hbs +19 -5
  172. package/src/generator/template/package.json.hbs +8 -2
  173. package/src/index.ts +3 -1
  174. package/src/neo4j/README.md +2 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupiterone/integration-sdk-cli",
3
- "version": "12.7.1",
3
+ "version": "12.8.3",
4
4
  "description": "The SDK for developing JupiterOne integrations",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -17,18 +17,20 @@
17
17
  "access": "public"
18
18
  },
19
19
  "scripts": {
20
+ "test": "jest",
20
21
  "prebuild:dist": "rm -rf dist && mkdir 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",
22
+ "build:dist": "tsc -p tsconfig.dist.json --declaration && npm run copy-files",
23
+ "prepack": "npm run build:dist",
24
+ "copy-files": "cp -r src/generator/template dist/src/generator/template && cp -r src/generator/stepTemplate dist/src/generator/stepTemplate && cp -r src/bocchi/templates dist/src/bocchi/templates",
24
25
  "plop": "plop --plopfile dist/src/generator/newIntegration.js"
25
26
  },
26
27
  "dependencies": {
27
- "@jupiterone/data-model": "^0.55.0",
28
- "@jupiterone/integration-sdk-core": "^12.7.1",
29
- "@jupiterone/integration-sdk-runtime": "^12.7.1",
28
+ "@jupiterone/data-model": "^0.61.3",
29
+ "@jupiterone/integration-sdk-core": "^12.8.3",
30
+ "@jupiterone/integration-sdk-runtime": "^12.8.3",
30
31
  "chalk": "^4",
31
32
  "commander": "^9.4.0",
33
+ "ejs": "^3.1.9",
32
34
  "fs-extra": "^10.1.0",
33
35
  "globby": "^11.0.0",
34
36
  "inquirer-checkbox-plus-prompt": "^1.4.2",
@@ -43,7 +45,7 @@
43
45
  "url-exists": "^1.0.3"
44
46
  },
45
47
  "devDependencies": {
46
- "@jupiterone/integration-sdk-private-test-utils": "^12.7.1",
48
+ "@jupiterone/integration-sdk-private-test-utils": "^12.8.3",
47
49
  "@pollyjs/adapter-node-http": "^6.0.5",
48
50
  "@pollyjs/core": "^6.0.5",
49
51
  "@pollyjs/persister-fs": "^6.0.5",
@@ -56,5 +58,5 @@
56
58
  "neo-forgery": "^2.0.0",
57
59
  "vis": "^4.21.0-EOL"
58
60
  },
59
- "gitHead": "e4d3dbe3b95d47e043d10f508355195a99ea4da3"
61
+ "gitHead": "aa9dfabc973f1b046424b742b34cafc91261c3f5"
60
62
  }
@@ -0,0 +1,95 @@
1
+ # Bocchi - a JupiterOne graph project generator
2
+
3
+ Developing a graph project from scratch for a new JupiterOne integration can be
4
+ time consuming, and typically involves writing a lot of boilerplate code that is
5
+ similar across all integrations. This is where Bocchi comes in. It's purpose is
6
+ to eliminate the need to write boilerplate code, as well as standardize much of
7
+ the logic and practices our graph projects use. Moreover, Bocchi allows us to
8
+ more quickly expand JupiterOne’s integration roster without spending a ton of
9
+ resources and development time.
10
+
11
+ Bocchi is a one-time use tool to be used when creating a new graph project. To
12
+ use Bocchi, a developer provides a JSON template that contains key components of
13
+ the new integration, such as instance configuration fields, the authentication
14
+ method, and the steps the new integration should run. Bocchi will interpret this
15
+ JSON template and generate a fully-functioning graph project - things like an
16
+ http-service client, step handlers, authentication functions, etc. will all be
17
+ written for the developer. After using Bocchi to generate a new graph project,
18
+ the only code the developer (may) need to write is the more involved,
19
+ vendor-specific logic.
20
+
21
+ To try Bocchi out, you can use the example template
22
+ [here](./docs/template/examples/signalSciences.json).
23
+
24
+ ## The Template
25
+
26
+ To create a new graph project using Bocchi, a developer needs to provide a JSON
27
+ template that Bocchi will interpret to generate graph code. The JSON template
28
+ defines key components of the integration, such as instance configuration
29
+ fields, the authentication method, as well as what steps the integration should
30
+ have. Moreoever, the steps outlined in the template contain information on what
31
+ API endpoints should be used for the step, as well as what entities and
32
+ relationships should be ingested.
33
+
34
+ <b>Currently, Bocchi only supports creating graph projects that ingest data from
35
+ REST APIs!</b>
36
+
37
+ Documentation for the JSON template can be found
38
+ [here](./docs/template/README.md).
39
+
40
+ Additionally, example template files can be found
41
+ [here](./docs/template/examples/).
42
+
43
+ ## Usage
44
+
45
+ ```
46
+ yarn j1-integration bocchi
47
+ ```
48
+
49
+ ## Additional Notes
50
+
51
+ ### Authentication
52
+
53
+ Bocchi currently supports two different methods of authentication for
54
+ integrations: Endpoint authentication and Config Value authentication.
55
+
56
+ Endpoint authentication is used when the product we're integrating with requires
57
+ an API request to a special endpoint to receive authentication headers that will
58
+ be used in all future requests. However, if the product does not require a
59
+ special authentication endpoint to be hit and instead something like a pre-made
60
+ API token can be sent on all API requests, then Config Value authentication can
61
+ be used.
62
+
63
+ Note that if Config Value authentication is used for the integration - that is,
64
+ authentication information is provided in the instance config - then the graph
65
+ project Bocchi generates will have an incomplete `verifyAuthentication()`
66
+ function in the client. This is because the graph-project generator does not
67
+ know what endpoint to use for verifying authentication.
68
+
69
+ <b>The developer will need to manually update the generated
70
+ verifyAuthentication() function and choose what endpoint should be used for
71
+ verifying authentication.</b>
72
+
73
+ ### Types
74
+
75
+ After running Bocchi, the generated graph project will have a `types.ts` file
76
+ that contains TS types for the entities the integration will produce. The types
77
+ however will be `any`. This is because Bocchi cannot guess what properties will
78
+ be on the different entities the integration will produce. So for prosperity and
79
+ to follow best coding practices, <b>The types should be updated manually by the
80
+ developer.</b>
81
+
82
+ ### Mapped Relationships
83
+
84
+ If a developer defines a mapped relationship(s) in the template, then they may
85
+ notice the following warning when running the integration locally:
86
+
87
+ ```
88
+ The following types were encountered but are not declared in the step's "types" field:
89
+ ```
90
+
91
+ If a step produces a mapped relationship(s), the developer will need to manually
92
+ add the `mappedRelationships` field in the generated stepMap for that step. The
93
+ `mappedRelationships` field will need to contain a valid
94
+ `StepMappedRelationshipMetadata` object (from
95
+ `@jupiterone/integration-sdk-core`).
@@ -0,0 +1,17 @@
1
+ import { Step, StepType } from '../utils/types';
2
+
3
+ export function stepTemplateHelper(step: Step) {
4
+ return {
5
+ stepTemplateFile: `${determineTypeOfStep(step)}.ts.hbs`,
6
+ };
7
+ }
8
+
9
+ function determineTypeOfStep(step: Step): string {
10
+ return step.response.responseType === 'SINGLETON'
11
+ ? step.parentAssociation
12
+ ? StepType.CHILD_SINGLETON
13
+ : StepType.SINGLETON
14
+ : step.parentAssociation
15
+ ? StepType.FETCH_CHILD_ENTITIES
16
+ : StepType.FETCH_ENTITIES;
17
+ }
@@ -0,0 +1,311 @@
1
+ import { NodePlopAPI } from 'plop';
2
+ import checkboxPlus from 'inquirer-checkbox-plus-prompt';
3
+ import path from 'path';
4
+ import { kebabCase } from 'lodash';
5
+ import {
6
+ yarnFormat,
7
+ yarnInstall,
8
+ yarnLint,
9
+ j1Document,
10
+ } from '../generator/actions';
11
+ import { Template } from './utils/types';
12
+ import * as fs from 'fs';
13
+ import { stepTemplateHelper } from './actions/steps';
14
+ import {
15
+ IntegrationInstanceConfigFieldMap,
16
+ RelationshipDirection,
17
+ generateRelationshipType,
18
+ } from '@jupiterone/integration-sdk-core';
19
+
20
+ function bocchi(plop: NodePlopAPI) {
21
+ plop.setActionType('yarnFormat', yarnFormat);
22
+ plop.setActionType('yarnInstall', yarnInstall);
23
+ plop.setActionType('yarnLint', yarnLint);
24
+ plop.setActionType('j1Document', j1Document);
25
+ plop.setPrompt('checkbox-plus', checkboxPlus);
26
+ plop.setHelper('getDirectRelationships', (step, options) => {
27
+ return step.directRelationships.map((relationship) => {
28
+ const step2 = options.data.root.template.steps.find(
29
+ (s) => s.entity._type === relationship.targetType,
30
+ );
31
+ return {
32
+ step,
33
+ sourceStep:
34
+ relationship.direction === RelationshipDirection.FORWARD
35
+ ? step
36
+ : step2,
37
+ targetStep:
38
+ relationship.direction === RelationshipDirection.FORWARD
39
+ ? step2
40
+ : step,
41
+ relationshipClass: relationship._class,
42
+ targetKey: relationship.targetKey,
43
+ forward: relationship.direction === RelationshipDirection.FORWARD,
44
+ };
45
+ });
46
+ });
47
+ plop.setHelper('getStepByType', (type, options) => {
48
+ return options.data.root.template.steps.find(
49
+ (s) => s.entity._type === type,
50
+ );
51
+ });
52
+ /**
53
+ * (_class, fromType, toType) => string
54
+ */
55
+ plop.setHelper('getRelationshipType', generateRelationshipType);
56
+ plop.setHelper('sanitizeUrlPath', (urlTemplate: string): string => {
57
+ const regex = /%parent\.(.+?)%/g;
58
+ for (const match of urlTemplate.matchAll(regex)) {
59
+ urlTemplate = urlTemplate.replace(
60
+ match[0],
61
+ '${parentEntity.' + match[1] + '}',
62
+ );
63
+ }
64
+ // looks at the urlTemplate provided and replaces "%nextToken%" with "${nextToken}",
65
+ // which is the string the client file will use to reference the nextToken
66
+ return urlTemplate.replace('%nextToken%', '${nextToken}');
67
+ });
68
+ plop.setHelper('escape', (data) => {
69
+ if (typeof data === 'string') {
70
+ return "'data'";
71
+ } else if (Array.isArray(data)) {
72
+ return '[' + data.toString() + ']';
73
+ } else {
74
+ return data;
75
+ }
76
+ });
77
+ plop.setHelper('sanitizeHttpMethod', (method: string = 'GET'): string => {
78
+ // if the user puts nothing in request.method, we need to default to GET
79
+ return method.toUpperCase();
80
+ });
81
+ plop.setHelper(
82
+ 'sanitizeHttpBody',
83
+ (body: Record<string, any>): Record<string, any> => {
84
+ // Also, we need to check if the body contains "%nextToken%" and replace it with
85
+ // "nextToken" since that is the var in the client file that references the nextToken.
86
+ for (const key in body) {
87
+ if (typeof body[key] === 'string') {
88
+ body[key] = body[key].replace('%nextToken%', '${nextToken}');
89
+ }
90
+ }
91
+ return body;
92
+ },
93
+ );
94
+ plop.setHelper('isSingletonRequest', (responseType: string): boolean => {
95
+ return responseType === 'SINGLETON';
96
+ });
97
+ plop.setHelper('configTypeToType', (type: string) => {
98
+ if (type.toLowerCase() === 'json') {
99
+ return 'Record<string, any>';
100
+ }
101
+ return type;
102
+ });
103
+ plop.setHelper(
104
+ 'requiredConfig',
105
+ (configFields: IntegrationInstanceConfigFieldMap) => {
106
+ return Object.entries(configFields)
107
+ .filter(([_, value]) => !value.optional)
108
+ .map(([key, _]) => key);
109
+ },
110
+ );
111
+ plop.setHelper('isNotEndpointAuth', (authStrategy: string): boolean => {
112
+ return authStrategy !== 'endpoint';
113
+ });
114
+ plop.setHelper(
115
+ 'sanitizeAuthObject',
116
+ (authObj: Record<string, any>): Record<string, any> => {
117
+ const regex = /%(response|config)\.(.+?)%/g;
118
+ for (const key in authObj) {
119
+ if (typeof authObj[key] === 'string') {
120
+ for (const match of authObj[key].matchAll(regex)) {
121
+ authObj[key] = authObj[key].replace(
122
+ match[0],
123
+ '${' +
124
+ (match[1] === 'config' ? 'this.' : '') +
125
+ match[1] +
126
+ '.' +
127
+ match[2] +
128
+ '}',
129
+ );
130
+ }
131
+ }
132
+ }
133
+ return authObj;
134
+ },
135
+ );
136
+
137
+ for (const partial of fs.readdirSync(
138
+ path.join(__dirname, 'templates/partials'),
139
+ )) {
140
+ plop.setPartial(
141
+ partial.replace('.hbs', ''),
142
+ fs.readFileSync(
143
+ path.join(__dirname, 'templates/partials', partial),
144
+ 'utf8',
145
+ ),
146
+ );
147
+ }
148
+
149
+ plop.setGenerator('bocchi', {
150
+ description: 'Create a new integration graph project',
151
+ prompts: async function (inquirer) {
152
+ const { vendorName } = await inquirer.prompt({
153
+ type: 'input',
154
+ name: 'vendorName',
155
+ message:
156
+ 'The integration vendor name (ex. AWS, Google Workspace, CrowdStrike)',
157
+ validate(input) {
158
+ if (!input) {
159
+ return 'Cannot be an empty string';
160
+ }
161
+ return true;
162
+ },
163
+ });
164
+
165
+ const { packageName } = await inquirer.prompt({
166
+ type: 'input',
167
+ name: 'packageName',
168
+ default: `@jupiterone/graph-${kebabCase(vendorName)}`,
169
+ message: 'The npm package name',
170
+ validate(input) {
171
+ if (!input) {
172
+ return 'Cannot be an empty string';
173
+ }
174
+ return true;
175
+ },
176
+ });
177
+
178
+ const { packageDescription } = await inquirer.prompt({
179
+ type: 'input',
180
+ name: 'packageDescription',
181
+ message: 'A description for package',
182
+ default: `An integration and graph conversion project for ${vendorName}`,
183
+ validate(input) {
184
+ if (!input) {
185
+ return 'Cannot be an empty string';
186
+ }
187
+ return true;
188
+ },
189
+ });
190
+
191
+ const { templateFile } = await inquirer.prompt({
192
+ type: 'input',
193
+ name: 'templateFile',
194
+ message: 'Template file for the integration',
195
+ validate(input) {
196
+ if (!input) {
197
+ return 'Cannot be an empty string';
198
+ }
199
+ return true;
200
+ },
201
+ });
202
+
203
+ return {
204
+ vendorName,
205
+ packageName,
206
+ packageDescription,
207
+ templateFile,
208
+ };
209
+ },
210
+ actions: function (data) {
211
+ if (!data) {
212
+ return [];
213
+ }
214
+
215
+ const { templateFile } = data;
216
+
217
+ // @jupiterone/graph-foo -> graph-foo
218
+ // graph-foo -> graph-foo
219
+ const directoryName = path.join(
220
+ process.cwd(),
221
+ path.basename(data.packageName),
222
+ );
223
+
224
+ const template: Template = JSON.parse(
225
+ fs.readFileSync(templateFile, 'utf8'),
226
+ );
227
+
228
+ const actions: any[] = [];
229
+
230
+ actions.push({
231
+ type: 'addMany',
232
+ destination: directoryName,
233
+ base: path.join(__dirname, '/templates/top-level'),
234
+ templateFiles: path.join(__dirname + '/templates/top-level/**'),
235
+ globOptions: { dot: true },
236
+ force: true,
237
+ data: { ...data, template },
238
+ });
239
+
240
+ for (const step of template.steps) {
241
+ const { stepTemplateFile } = stepTemplateHelper(step);
242
+ actions.push({
243
+ type: 'add',
244
+ path: path.join(
245
+ directoryName,
246
+ path.normalize(`src/steps/${kebabCase(step.id)}/index.ts`),
247
+ ),
248
+ templateFile: path.join(
249
+ __dirname,
250
+ `templates/steps/${stepTemplateFile}`,
251
+ ),
252
+ data: { template, step },
253
+ force: true,
254
+ });
255
+
256
+ actions.push({
257
+ type: 'add',
258
+ path: path.join(
259
+ directoryName,
260
+ path.normalize(`src/steps/${kebabCase(step.id)}/index.test.ts`),
261
+ ),
262
+ templateFile: path.join(
263
+ __dirname,
264
+ `templates/steps/index.test.ts.hbs`,
265
+ ),
266
+ data: { template, step },
267
+ force: true,
268
+ });
269
+
270
+ actions.push({
271
+ type: 'add',
272
+ path: path.join(
273
+ directoryName,
274
+ path.normalize(`docs/spec/${kebabCase(step.id)}/index.ts`),
275
+ ),
276
+ templateFile: path.join(__dirname, `templates/steps/spec.ts.hbs`),
277
+ data: { template, step },
278
+ force: true,
279
+ });
280
+ }
281
+
282
+ actions.push({
283
+ type: 'yarnInstall',
284
+ path: directoryName,
285
+ verbose: true,
286
+ });
287
+
288
+ actions.push({
289
+ type: 'j1Document',
290
+ path: directoryName,
291
+ verbose: true,
292
+ });
293
+
294
+ actions.push({
295
+ type: 'yarnFormat',
296
+ path: directoryName,
297
+ verbose: true,
298
+ });
299
+
300
+ actions.push({
301
+ type: 'yarnLint',
302
+ path: directoryName,
303
+ verbose: true,
304
+ });
305
+
306
+ return actions;
307
+ },
308
+ });
309
+ }
310
+
311
+ module.exports = bocchi;
@@ -0,0 +1,140 @@
1
+ # The Template File
2
+
3
+ In order to generate the graph project code, Bocchi interprets a
4
+ developer-provided JSON template file. This JSON template defines key components
5
+ of the integration, such as instance configuration fields, the authentication
6
+ method, as well as what steps the integration should have.
7
+
8
+ Contents:
9
+
10
+ - [Format](#format)
11
+ - [Fields](#fields)
12
+ - [Instance Config Fields](#instance-config-fields)
13
+ - [Base URL](#base-url)
14
+ - [Token Bucket](#token-bucket)
15
+ - [Authentication](#authentication)
16
+ - [Steps](#steps)
17
+
18
+ ## Format
19
+
20
+ ```ts
21
+ {
22
+ instanceConfigFields: Object,
23
+ baseUrl: string,
24
+ tokenBucket: Object
25
+ authentication: Object,
26
+ steps: Array<Object>
27
+ }
28
+ ```
29
+
30
+ ## Fields
31
+
32
+ ### Instance Config Fields
33
+
34
+ Lists out the config fields used by this integration.
35
+
36
+ Schema:
37
+
38
+ | | |
39
+ | -------- | ---------------------- |
40
+ | Property | `instanceConfigFields` |
41
+ | Type | `Object` |
42
+ | Required | `true` |
43
+
44
+ ```ts
45
+ {
46
+ [configFieldName]: {
47
+ type?: 'string' | 'json' | 'boolean',
48
+ mask?: Boolean,
49
+ optional?: Boolean
50
+ }
51
+ }
52
+ ```
53
+
54
+ Example:
55
+
56
+ ```json
57
+ {
58
+ "accessKey": {
59
+ "type": "string"
60
+ },
61
+ "accessKeyId": {
62
+ "type": "string",
63
+ "optional": false
64
+ },
65
+ "example": {
66
+ "type": "boolean",
67
+ "mask": true,
68
+ "optional": true
69
+ }
70
+ }
71
+ ```
72
+
73
+ ### Base URL
74
+
75
+ Template for the base URL of the API.
76
+
77
+ Schema:
78
+
79
+ | | |
80
+ | ------------------- | --------- |
81
+ | Property | `baseUrl` |
82
+ | Type | `string` |
83
+ | Required | `true` |
84
+ | Available templates | `config` |
85
+
86
+ Examples:
87
+
88
+ `https://api.github.com/octocat`
89
+
90
+ `https://%config.tenant%.example.com`
91
+
92
+ ### Token Bucket
93
+
94
+ Allows you to define a rate limit across all steps. Useful for an API where the
95
+ rate limit is per API key rather than per endpoint.
96
+
97
+ Can be used in combination with step-level token buckets.
98
+
99
+ Schema:
100
+
101
+ | | |
102
+ | -------- | ------------- |
103
+ | Property | `tokenBucket` |
104
+ | Type | `Object` |
105
+ | Required | `false` |
106
+
107
+ ```ts
108
+ {
109
+ maximumCapacity: number,
110
+ refillRate: number
111
+ }
112
+ ```
113
+
114
+ Examples:
115
+
116
+ 10 API calls / second
117
+
118
+ ```json
119
+ {
120
+ "maximumCapacity": 10,
121
+ "refillRate": 10
122
+ }
123
+ ```
124
+
125
+ 10 API calls / second, 50 Burst
126
+
127
+ ```json
128
+ {
129
+ "maximumCapacity": 50,
130
+ "refillRate": 10
131
+ }
132
+ ```
133
+
134
+ ### Authentication
135
+
136
+ [Authentication](./authentication.md)
137
+
138
+ ### Steps
139
+
140
+ [Steps](./steps.md)