@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
@@ -0,0 +1,114 @@
1
+ # JupiterOne Integration
2
+
3
+ Learn about the data ingested, benefits of this integration, and how to use it
4
+ with JupiterOne in the [integration documentation](docs/jupiterone.md).
5
+
6
+ ## Development
7
+
8
+ ### Prerequisites
9
+
10
+ 1. Install [Node.js](https://nodejs.org/) using the
11
+ [installer](https://nodejs.org/en/download/) or a version manager such as
12
+ [nvm](https://github.com/nvm-sh/nvm) or [fnm](https://github.com/Schniz/fnm).
13
+ 2. Install [`yarn`](https://yarnpkg.com/getting-started/install) or
14
+ [`npm`](https://github.com/npm/cli#installation) to install dependencies.
15
+ 3. Install dependencies with `yarn install`.
16
+ 4. Register an account in the system this integration targets for ingestion and
17
+ obtain API credentials.
18
+ 5. `cp .env.example .env` and add necessary values for runtime configuration.
19
+
20
+ When an integration executes, it needs API credentials and any other
21
+ configuration parameters necessary for its work (provider API credentials,
22
+ data ingestion parameters, etc.). The names of these parameters are defined
23
+ by the `IntegrationInstanceConfigFieldMap`in `src/config.ts`. When the
24
+ integration is executed outside the JupiterOne managed environment (local
25
+ development or on-prem), values for these parameters are read from Node's
26
+ `process.env` by converting config field names to constant case. For example,
27
+ `clientId` is read from `process.env.CLIENT_ID`.
28
+
29
+ The `.env` file is loaded into `process.env` before the integration code is
30
+ executed. This file is not required should you configure the environment
31
+ another way. `.gitignore` is configured to avoid committing the `.env` file.
32
+
33
+ ### Running the integration
34
+
35
+ #### Running directly
36
+
37
+ 1. `yarn start` to collect data
38
+ 2. `yarn graph` to show a visualization of the collected data
39
+ 3. `yarn j1-integration -h` for additional commands
40
+
41
+ #### Running with Docker
42
+
43
+ Create an integration instance for the integration in JupiterOne. With an
44
+ **JupiterOne API Key** scoped to the integration or an API Key with permissions
45
+ to synchronize data and the **Integration Instance ID**:
46
+
47
+ 1. `docker build -t $IMAGE_NAME .`
48
+ 2. `docker run -e "JUPITERONE_API_KEY=<JUPITERONE_API_KEY>" -e "JUPITERONE_ACCOUNT=<JUPITERONE_ACCOUNT> -e "INTEGRATION_INSTANCE_ID=<INTEGRATION_INSTANCE_ID>" "JUPITERONE_API_BASE_URL=<JUPITERONE_API_BASE_URL>" $IMAGE_NAME`
49
+
50
+ ### Making Contributions
51
+
52
+ Start by taking a look at the source code. The integration is basically a set of
53
+ functions called steps, each of which ingests a collection of resources and
54
+ relationships. The goal is to limit each step to as few resource types as
55
+ possible so that should the ingestion of one type of data fail, it does not
56
+ necessarily prevent the ingestion of other, unrelated data. That should be
57
+ enough information to allow you to get started coding!
58
+
59
+ See the
60
+ [SDK development documentation](https://github.com/JupiterOne/sdk/blob/main/docs/integrations/development.md)
61
+ for a deep dive into the mechanics of how integrations work.
62
+
63
+ See [docs/development.md](docs/development.md) for any additional details about
64
+ developing this integration.
65
+
66
+ ## Testing the integration
67
+
68
+ Ideally, all major calls to the API and converter functions would be tested. You
69
+ can run the tests with `yarn test`, and you can run the tests as they execute in
70
+ the CI/CD environment with `yarn test:ci` (adds linting and type-checking to
71
+ `yarn test`). If you have a valid runtime configuration, you can run the tests
72
+ with your credentials using `yarn test:env`.
73
+
74
+ For more details on setting up tests, and specifically on using recordings to
75
+ simulate API responses, see `test/README.md`.
76
+
77
+ ### Changelog
78
+
79
+ The history of this integration's development can be viewed at
80
+ [CHANGELOG.md](CHANGELOG.md).
81
+
82
+ ## Versioning this project
83
+
84
+ This project is versioned using [auto](https://intuit.github.io/auto/).
85
+
86
+ Versioning and publishing to NPM are now handled via adding GitHub labels to
87
+ pull requests. The following labels should be used for this process:
88
+
89
+ - patch
90
+ - minor
91
+ - major
92
+ - release
93
+
94
+ For each pull request, the degree of change should be registered by applying the
95
+ appropriate label of patch, minor, or major. This allows the repository to keep
96
+ track of the highest degree of change since the last release. When ready to
97
+ publish to NPM, the PR should have both its appropriate patch, minor, or major
98
+ label applied as well as a release label. The release label will denote to the
99
+ system that we need to publish to NPM and will correctly version based on the
100
+ highest degree of change since the last release, package the project, and
101
+ publish it to NPM.
102
+
103
+ In order to successfully version and publish to NPM we need access to two
104
+ secrets: a valid NPM token for publishing and a GitHub token for querying the
105
+ repo and pushing version changes. For JupiterOne projects please put in a ticket
106
+ with security to have the repository correctly granted access. For external
107
+ projects, please provide secrets with access to your own NPM and GitHub
108
+ accounts. The secret names should be set to NPM_AUTH_TOKEN and
109
+ AUTO_GITHUB_PAT_TOKEN respectively (or the action can be updated to accommodate
110
+ different naming conventions).
111
+
112
+ We are not currently using the functionality for auto to update the CHANGELOG.
113
+ As such, please remember to update CHANGELOG.md with the appropriate version,
114
+ date, and changes.
@@ -0,0 +1,28 @@
1
+ # Development
2
+
3
+ Add details here to give a brief overview of how to work with the provider APIs.
4
+ Please reference any SDKs or API docs used to help build the integration here.
5
+
6
+ ## Prerequisites
7
+
8
+ Supply details about software or tooling (like maybe Docker or Terraform) that
9
+ is needed for development here.
10
+
11
+ Please supply references to documentation that details how to install those
12
+ dependencies here.
13
+
14
+ Tools like Node.js and NPM are already covered in the [README](../README.md) so
15
+ don't bother documenting that here.
16
+
17
+ ## Provider account setup
18
+
19
+ Please provide information about the steps needed to create an account with a
20
+ provider. Images and references to a provider's documentation is very helpful
21
+ for new developers picking up your work.
22
+
23
+ ## Authentication
24
+
25
+ Supply details here for information on how to authenticate with a provider so
26
+ that developers have an idea of what's needed to hit APIs. It may be useful to
27
+ provide explanations for each value specified in the
28
+ [`IntegrationInstanceConfigFieldMap`](../src/config.ts).
@@ -0,0 +1 @@
1
+ # {{titleCase vendorName}}
@@ -0,0 +1,14 @@
1
+ import { IntegrationSpecConfig } from '@jupiterone/integration-sdk-core';
2
+ import { IntegrationConfig } from '../../src/config';
3
+ {{#each template.steps}}
4
+ import { {{camelCase entity.name}}Spec } from './{{kebabCase id}}';
5
+ {{/each}}
6
+
7
+ export const invocationConfig: IntegrationSpecConfig<IntegrationConfig> =
8
+ {
9
+ integrationSteps: [
10
+ {{#each template.steps}}
11
+ ...{{camelCase entity.name}}Spec,
12
+ {{/each}}
13
+ ],
14
+ };
@@ -0,0 +1 @@
1
+ module.exports = require('@jupiterone/integration-sdk-dev-tools/config/husky');
@@ -0,0 +1 @@
1
+ module.exports = require('@jupiterone/integration-sdk-dev-tools/config/jest');
@@ -0,0 +1,16 @@
1
+ ---
2
+ sourceId: managed:{{kebabCase vendorName}}
3
+ integrationDefinitionId: '${integration_definition_id}'
4
+ questions:
5
+ []
6
+ # - id: integration-question-template-replace-me
7
+ # title: What kinds of questions will this integration support?
8
+ # description:
9
+ # TODO Every integration should contribute questions! Please be careful
10
+ # to replace this before deploying the integration to JupiterOne.
11
+ # queries:
12
+ # - name: good
13
+ # query: |
14
+ # find * with _integrationDefinitionId = '${integration_definition_id}'
15
+ # tags:
16
+ # - template
@@ -0,0 +1 @@
1
+ module.exports = require('@jupiterone/integration-sdk-dev-tools/config/lint-staged');
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "version": "0.0.1",
4
+ "description": "{{packageDescription}}",
5
+ "license": "MPL-2.0",
6
+ "main": "dist/src/index.js",
7
+ "types": "dist/src/index.d.ts",
8
+ "files": [
9
+ "src",
10
+ "jupiterone"
11
+ ],
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "engines": {
16
+ "node": ">=18.0.0 <20.x"
17
+ },
18
+ "scripts": {
19
+ "start": "j1-integration collect",
20
+ "graph": "j1-integration visualize",
21
+ "graph:types": "j1-integration visualize-types",
22
+ "graph:spec": "j1-integration visualize-types --project-path docs/spec --output-file ./.j1-integration/types-graph/index.spec.html",
23
+ "graph:dependencies": "j1-integration visualize-dependencies",
24
+ "validate:questions:dry": "j1-integration validate-question-file --dry-run",
25
+ "validate:questions": "j1-integration validate-question-file -a $MANAGED_QUESTIONS_JUPITERONE_ACCOUNT_ID -k $MANAGED_QUESTIONS_JUPITERONE_API_KEY",
26
+ "lint": "eslint . --cache --fix --ext .ts,.tsx",
27
+ "format": "prettier --write \"**/*.{ts,js,json,css,md,yml}\"",
28
+ "format:check": "prettier --check \"**/*.{ts,js,json,css,md,yml}\"",
29
+ "type-check": "tsc",
30
+ "test": "jest",
31
+ "test:env": "LOAD_ENV=1 yarn test",
32
+ "test:ci": "yarn format:check && yarn lint && yarn type-check && yarn test",
33
+ "build": "tsc -p tsconfig.dist.json --declaration && cp README.md dist/README.md && cp -r jupiterone/ dist/jupiterone/",
34
+ "build:docker": "tsc --declaration false --emitDeclarationOnly false -p tsconfig.dist.json",
35
+ "prepush": "yarn format:check && yarn lint && yarn type-check && jest --changedSince main",
36
+ "postversion": "cp package.json ./dist/package.json"
37
+ },
38
+ "dependencies": {
39
+ "@jupiterone/integration-sdk-http-client": "^12.7.0"
40
+ },
41
+ "peerDependencies": {
42
+ "@jupiterone/integration-sdk-core": "^12.7.0"
43
+ },
44
+ "devDependencies": {
45
+ "@jupiterone/integration-sdk-core": "^12.7.0",
46
+ "@jupiterone/integration-sdk-dev-tools": "^12.7.0",
47
+ "@jupiterone/integration-sdk-testing": "^12.7.0"
48
+ }
49
+ }
@@ -0,0 +1 @@
1
+ module.exports = require('@jupiterone/integration-sdk-dev-tools/config/prettier');
@@ -0,0 +1,116 @@
1
+ import { BaseAPIClient } from '@jupiterone/integration-sdk-http-client';
2
+ import { IntegrationConfig } from './config';
3
+ import {
4
+ {{#each template.steps}}
5
+ {{pascalCase entity.name}},
6
+ {{/each}}
7
+ } from './steps/types';
8
+ import { Entity, IntegrationLogger } from '@jupiterone/integration-sdk-core';
9
+
10
+ export type ResourceIteratee<T> = (each: T) => Promise<void> | void;
11
+
12
+ export class APIClient extends BaseAPIClient {
13
+ private authHeaders: Record<string, string>;
14
+
15
+ constructor(readonly config: IntegrationConfig, logger: IntegrationLogger) {
16
+ super({
17
+ baseUrl: '{{template.baseUrl}}',
18
+ logger,
19
+ {{#if template.tokenBucket}}
20
+ tokenBucket: {
21
+ maximumCapacity: {{template.tokenBucket.maximumCapacity}},
22
+ refillRate: {{template.tokenBucket.refillRate}}
23
+ }
24
+ {{/if}}
25
+ });
26
+ }
27
+
28
+ {{#if (isNotEndpointAuth template.authentication.strategy)}}
29
+ protected getAuthorizationHeaders(): Record<string, string> {
30
+ return { {{#each (sanitizeAuthObject template.authentication.authHeaders)}}'{{@key}}': `{{this}}`,{{/each}} };
31
+ }
32
+ {{else}}
33
+ protected async getAuthorizationHeaders(): Promise<Record<string, string>> {
34
+ const response = await (await this.retryableRequest(
35
+ `${this.baseUrl}{{template.authentication.params.path}}`,
36
+ {
37
+ authorize: false, // needed only for authentication call
38
+ headers: { {{#each (sanitizeAuthObject template.authentication.params.headers)}}'{{@key}}': `{{this}}`,{{/each}} },
39
+ method: '{{sanitizeHttpMethod template.authentication.params.method}}',
40
+ {{#if request.params}}
41
+ body: { {{#each (sanitizeAuthObject template.authentication.params.body)}}{{@key}}: {{this}},{{/each}} }
42
+ {{/if}}
43
+ }
44
+ )).json();
45
+ return { {{#each (sanitizeAuthObject template.authentication.authHeaders)}}'{{@key}}': `{{this}}`,{{/each}} }
46
+ }
47
+ {{/if}}
48
+
49
+ public async verifyAuthentication(): Promise<void> {
50
+ {{#if (isNotEndpointAuth template.authentication.strategy)}}
51
+ // TODO: implement
52
+ await new Promise(() => { throw new Error('Unimplemented: Pick an API call to use for validation!') });
53
+ {{else}}
54
+ await this.getAuthorizationHeaders();
55
+ {{/if}}
56
+ }
57
+ {{#each template.steps}}
58
+
59
+ {{#if (isSingletonRequest response.responseType)}}
60
+ public async get{{pascalCase entity.name}}({{#if parentAssociation}}parentEntity: Entity{{/if}}): Promise<{{pascalCase entity.name}}> {
61
+ const url = `${this.baseUrl}{{{sanitizeUrlPath request.urlTemplate}}}`;
62
+ const response = await (await this.retryableRequest(
63
+ url,
64
+ {
65
+ method: '{{sanitizeHttpMethod request.method}}',
66
+ {{#if request.params}}
67
+ body: { {{#each (sanitizeHttpBody request.params)}}'{{@key}}': `{{this}}`,{{/each}} }
68
+ {{/if}}
69
+ }
70
+ )).json();
71
+ return response.{{response.dataPath}} as {{pascalCase entity.name}};
72
+ }
73
+ {{else}}
74
+ public async iterate{{pascalCase entity.name}}s(
75
+ {{#if parentAssociation}}
76
+ parentEntity: Entity,
77
+ {{/if}}
78
+ iteratee: ResourceIteratee<{{pascalCase entity.name}}>
79
+ ): Promise<void> {
80
+ {{#if response.nextTokenPath}}
81
+ let nextToken: string | undefined;
82
+ do {
83
+ {{/if}}
84
+ const url = `${this.baseUrl}{{{sanitizeUrlPath request.urlTemplate}}}`;
85
+ const response = await (await this.retryableRequest(
86
+ url,
87
+ {
88
+ method: '{{sanitizeHttpMethod request.method}}',
89
+ {{#if request.params}}
90
+ body: { {{#each (sanitizeHttpBody request.params)}}'{{@key}}': `{{this}}`,{{/each}} }
91
+ {{/if}}
92
+ }
93
+ )).json();
94
+ const resources = response.{{response.dataPath}} as {{pascalCase entity.name}}[];
95
+ for (const resource of resources) {
96
+ await iteratee(resource);
97
+ }
98
+ {{#if response.nextTokenPath}}
99
+ nextToken = response.{{response.nextTokenPath}};
100
+ } while (nextToken);
101
+ {{/if}}
102
+ }
103
+ {{/if}}
104
+ {{/each}}
105
+ }
106
+
107
+ let client: APIClient | undefined;
108
+
109
+ export function createAPIClient(
110
+ config: IntegrationConfig,
111
+ logger: IntegrationLogger
112
+ ): APIClient {
113
+ return client
114
+ ? client
115
+ : new APIClient(config, logger);
116
+ }
@@ -0,0 +1,41 @@
1
+ {{#with template}}
2
+ import {
3
+ IntegrationExecutionContext,
4
+ IntegrationValidationError,
5
+ IntegrationInstanceConfigFieldMap,
6
+ } from '@jupiterone/integration-sdk-core';
7
+ import { createAPIClient } from './client';
8
+
9
+ export const instanceConfigFields = {
10
+ {{#each instanceConfigFields}}
11
+ {{@key}}: {
12
+ {{#if type}}type: '{{type}}',{{/if}}
13
+ {{#if mask}}mask: {{mask}},{{/if}}
14
+ {{#if optional}}optional: {{optional}},{{/if}}
15
+ },
16
+ {{/each}}
17
+ } satisfies IntegrationInstanceConfigFieldMap;
18
+
19
+ export interface IntegrationConfig {
20
+ {{#each instanceConfigFields}}
21
+ {{@key}}: {{configTypeToType type}};
22
+ {{/each}}
23
+ }
24
+
25
+ export async function validateInvocation(
26
+ context: IntegrationExecutionContext<IntegrationConfig>,
27
+ ) {
28
+ const { config } = context.instance;
29
+
30
+ {{#with (requiredConfig instanceConfigFields)}}
31
+ {{#if this.length}}
32
+ if ({{#each this}}!config.{{this}}{{#unless @last}} || {{/unless}}{{/each}}) {
33
+ throw new IntegrationValidationError('Config requires all of{{#each this}} {{this}}{{/each}}');
34
+ }
35
+ {{/if}}
36
+ {{/with}}
37
+
38
+ const apiClient = createAPIClient(config);
39
+ await apiClient.verifyAuthentication();
40
+ }
41
+ {{/with}}
@@ -0,0 +1,6 @@
1
+ import { invocationConfig as implementedConfig } from '.';
2
+ import { invocationConfig as specConfig } from '../docs/spec';
3
+
4
+ test('implemented integration should match spec', () => {
5
+ expect(implementedConfig).toImplementSpec(specConfig, { requireSpec: true });
6
+ });
@@ -0,0 +1,14 @@
1
+ import { IntegrationInvocationConfig } from '@jupiterone/integration-sdk-core';
2
+ import { integrationSteps } from './steps';
3
+ import {
4
+ validateInvocation,
5
+ IntegrationConfig,
6
+ instanceConfigFields,
7
+ } from './config';
8
+
9
+ export const invocationConfig: IntegrationInvocationConfig<IntegrationConfig> =
10
+ {
11
+ instanceConfigFields,
12
+ validateInvocation,
13
+ integrationSteps,
14
+ };
@@ -0,0 +1,60 @@
1
+ {{#with template}}
2
+ import {
3
+ RelationshipClass,
4
+ StepEntityMetadata,
5
+ StepRelationshipMetadata,
6
+ } from '@jupiterone/integration-sdk-core';
7
+
8
+ export const Steps = {
9
+ {{#each steps}}
10
+ {{constantCase id}}: '{{id}}',
11
+ {{#if directRelationships}}
12
+ {{#with (getDirectRelationships this)}}
13
+ {{#each this}}
14
+ BUILD_{{constantCase sourceStep.entity.name}}_{{constantCase targetStep.entity.name}}_RELATIONSHIPS: 'build-{{kebabCase sourceStep.entity.name}}-{{kebabCase targetStep.entity.name}}-relationships',
15
+ {{/each}}
16
+ {{/with}}
17
+ {{/if}}
18
+ {{#if mappedRelationships}}
19
+ BUILD_{{constantCase entity.name}}_MAPPED_RELATIONSHIPS: 'build-{{kebabCase entity.name}}-mapped-relationships',
20
+ {{/if}}
21
+ {{/each}}
22
+ } satisfies Record<string, string>;
23
+
24
+ export const Entities = {
25
+ {{#each steps}}
26
+ {{#with entity}}
27
+ {{constantCase name}}: {
28
+ resourceName: '{{name}}',
29
+ _type: '{{_type}}',
30
+ _class: ['{{_class}}'],
31
+ },
32
+ {{/with}}
33
+ {{/each}}
34
+ } satisfies Record<string, StepEntityMetadata>;
35
+
36
+ export const Relationships = {
37
+ {{#each steps}}
38
+ {{#if parentAssociation}}
39
+ {{#with (getStepByType parentAssociation.parentEntityType)}}{{constantCase entity.name}}{{/with}}_{{constantCase parentAssociation.relationshipClass}}_{{constantCase entity.name}}: {
40
+ sourceType: Entities.{{#with (getStepByType parentAssociation.parentEntityType)}}{{constantCase entity.name}}{{/with}}._type,
41
+ _class: RelationshipClass.{{constantCase parentAssociation.relationshipClass}},
42
+ targetType: Entities.{{constantCase entity.name}}._type,
43
+ _type: '{{getRelationshipType parentAssociation.relationshipClass parentAssociation.parentEntityType entity._type}}',
44
+ },
45
+ {{/if}}
46
+ {{#if directRelationships}}
47
+ {{#with (getDirectRelationships this)}}
48
+ {{#each this}}
49
+ {{constantCase sourceStep.entity.name}}_{{constantCase relationshipClass}}_{{constantCase targetStep.entity.name}}: {
50
+ sourceType: Entities.{{constantCase sourceStep.entity.name}}._type,
51
+ _class: RelationshipClass.{{constantCase relationshipClass}},
52
+ targetType: Entities.{{constantCase targetStep.entity.name}}._type,
53
+ _type: '{{getRelationshipType relationshipClass sourceStep.entity._type targetStep.entity._type}}',
54
+ },
55
+ {{/each}}
56
+ {{/with}}
57
+ {{/if}}
58
+ {{/each}}
59
+ } satisfies Record<string, StepRelationshipMetadata>;
60
+ {{/with}}
@@ -0,0 +1,37 @@
1
+ {{#with template}}
2
+ import { createIntegrationEntity } from '@jupiterone/integration-sdk-core';
3
+ import { Entities } from './constants';
4
+ import {
5
+ {{#each steps}}
6
+ {{pascalCase entity.name}},
7
+ {{/each}}
8
+ } from './types';
9
+
10
+ {{#each steps}}
11
+ {{#with entity}}
12
+ export function create{{pascalCase name}}Entity(data: {{pascalCase name}}) {
13
+ return createIntegrationEntity({
14
+ entityData: {
15
+ source: data,
16
+ assign: {
17
+ _key: data.{{_keyPath}},
18
+ _type: Entities.{{constantCase name}}._type,
19
+ _class: Entities.{{constantCase name}}._class,
20
+ {{#if staticFields}}
21
+ {{#each staticFields}}
22
+ {{@key}}: {{{escape this}}},
23
+ {{/each}}
24
+ {{/if}}
25
+ {{#if fieldMappings}}
26
+ {{#each fieldMappings}}
27
+ {{@key}}: data.{{this}},
28
+ {{/each}}
29
+ {{/if}}
30
+ },
31
+ },
32
+ });
33
+ }
34
+ {{/with}}
35
+
36
+ {{/each}}
37
+ {{/with}}
@@ -0,0 +1,13 @@
1
+ {{#with template}}
2
+ {{#each steps}}
3
+ import { {{camelCase entity.name}}Steps } from './{{id}}';
4
+ {{/each}}
5
+
6
+ const integrationSteps = [
7
+ {{#each steps}}
8
+ ...{{camelCase entity.name}}Steps,
9
+ {{/each}}
10
+ ];
11
+
12
+ export { integrationSteps };
13
+ {{/with}}
@@ -0,0 +1,6 @@
1
+ {{#with template}}
2
+ {{#each steps}}
3
+ export type {{pascalCase entity.name}} = any;
4
+
5
+ {{/each}}
6
+ {{/with}}
@@ -0,0 +1,5 @@
1
+ # Testing Integrations
2
+
3
+ For information on testing integrations see:
4
+
5
+ [Testing Integrations](https://github.com/JupiterOne/sdk/blob/main/docs/integrations/testing.md)
@@ -0,0 +1,30 @@
1
+ import { IntegrationInvocationConfig } from '@jupiterone/integration-sdk-core';
2
+ import { StepTestConfig } from '@jupiterone/integration-sdk-testing';
3
+ import * as dotenv from 'dotenv';
4
+ import * as path from 'path';
5
+ import { invocationConfig } from '../src';
6
+ import { IntegrationConfig } from '../src/config';
7
+
8
+ if (process.env.LOAD_ENV) {
9
+ dotenv.config({
10
+ path: path.join(__dirname, '../.env'),
11
+ });
12
+ }
13
+
14
+ {{#each template.instanceConfigFields}}
15
+ const DEFAULT_{{constantCase @key}} = '';
16
+ {{/each}}
17
+
18
+ export const integrationConfig: IntegrationConfig = {
19
+ {{#each template.instanceConfigFields}}
20
+ {{camelCase @key}}: process.env.{{constantCase @key}} ?? DEFAULT_{{constantCase @key}},
21
+ {{/each}}
22
+ };
23
+
24
+ export function buildStepTestConfigForStep(stepId: string): StepTestConfig {
25
+ return {
26
+ stepId,
27
+ instanceConfig: integrationConfig,
28
+ invocationConfig: invocationConfig as IntegrationInvocationConfig,
29
+ };
30
+ }
@@ -0,0 +1,74 @@
1
+ import {
2
+ setupRecording,
3
+ Recording,
4
+ SetupRecordingInput,
5
+ mutations,
6
+ } from '@jupiterone/integration-sdk-testing';
7
+
8
+ export { Recording };
9
+
10
+ export function setupProjectRecording(
11
+ input: Omit<SetupRecordingInput, 'mutateEntry'>,
12
+ ): Recording {
13
+ return setupRecording({
14
+ ...input,
15
+ redactedRequestHeaders: ['Authorization'],
16
+ redactedResponseHeaders: ['set-cookie'],
17
+ mutateEntry: mutations.unzipGzippedRecordingEntry,
18
+ /*mutateEntry: (entry) => {
19
+ redact(entry);
20
+ },*/
21
+ });
22
+ }
23
+
24
+ // a more sophisticated redaction example below:
25
+
26
+ /*
27
+ function getRedactedOAuthResponse() {
28
+ return {
29
+ access_token: '[REDACTED]',
30
+ expires_in: 9999,
31
+ token_type: 'Bearer',
32
+ };
33
+ }
34
+
35
+ function redact(entry): void {
36
+ if (entry.request.postData) {
37
+ entry.request.postData.text = '[REDACTED]';
38
+ }
39
+
40
+ if (!entry.response.content.text) {
41
+ return;
42
+ }
43
+
44
+ //let's unzip the entry so we can modify it
45
+ mutations.unzipGzippedRecordingEntry(entry);
46
+
47
+ //we can just get rid of all response content if this was the token call
48
+ const requestUrl = entry.request.url;
49
+ if (requestUrl.match(/oauth\/token/)) {
50
+ entry.response.content.text = JSON.stringify(getRedactedOAuthResponse());
51
+ return;
52
+ }
53
+
54
+ //if it wasn't a token call, parse the response text, removing any carriage returns or newlines
55
+ const responseText = entry.response.content.text;
56
+ const parsedResponseText = JSON.parse(responseText.replace(/\r?\n|\r/g, ''));
57
+
58
+ //now we can modify the returned object as desired
59
+ //in this example, if the return text is an array of objects that have the 'tenant' property...
60
+ if (parsedResponseText[0]?.tenant) {
61
+ for (let i = 0; i < parsedResponseText.length; i++) {
62
+ parsedResponseText[i].client_secret = '[REDACTED]';
63
+ parsedResponseText[i].jwt_configuration = '[REDACTED]';
64
+ parsedResponseText[i].signing_keys = '[REDACTED]';
65
+ parsedResponseText[i].encryption_key = '[REDACTED]';
66
+ parsedResponseText[i].addons = '[REDACTED]';
67
+ parsedResponseText[i].client_metadata = '[REDACTED]';
68
+ parsedResponseText[i].mobile = '[REDACTED]';
69
+ parsedResponseText[i].native_social_login = '[REDACTED]';
70
+ }
71
+ }
72
+
73
+ entry.response.content.text = JSON.stringify(parsedResponseText);
74
+ } */
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "include": ["src", "package.json"],
4
+ "exclude": [
5
+ "dist",
6
+ "**/*.test.ts",
7
+ "**/*.test.js",
8
+ "**/*/__tests__/**/*.ts",
9
+ "**/*/__tests__/**/*.js",
10
+ "**/*/__mocks__/**/*.ts",
11
+ "**/*/__mocks__/**/*.js"
12
+ ]
13
+ }