### Create and Setup Integration Project Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Steps to create a new integration repository using a template and install dependencies. This involves using the GitHub CLI or manually cloning from a template repository. ```sh gh repo create graph-$INTEGRATION_NAME --public \ --clone \ --template=https://github.com/jupiterone/integration-template cd graph-$INTEGRATION_NAME npm install ``` ```sh git clone https://github.com/$USERNAME/$REPO_NAME cd $REPO_NAME npm install ``` -------------------------------- ### Update Environment Example File (.env.example Diff) Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Updates the `.env.example` file to reflect the new configuration variables, such as replacing `CLIENT_ID` and `CLIENT_SECRET` with `ACCESS_TOKEN`. ```diff - CLIENT_ID= - CLIENT_SECRET= + ACCESS_TOKEN= ``` -------------------------------- ### Running the Integration Commands Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Commands to run the integration project after setup. `npm run start` collects data, and `npm run graph` visualizes the collected results. ```sh npm run start ``` ```sh npm run graph ``` -------------------------------- ### Install node-fetch Dependency Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Installs the `node-fetch` package, a lightweight HTTP client for making requests in Node.js environments, as a dependency for the integration. ```Shell npm install node-fetch ``` -------------------------------- ### Copy Environment File (Shell) Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Copies the example environment file (`.env.example`) to create the actual environment file (`.env`) for local development. It's crucial to keep `.env` out of version control. ```sh cp .env.example .env ``` -------------------------------- ### Define Integration Steps Export Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Defines the main export for integration steps, combining steps from different modules like 'account' and 'access'. The example shows how to selectively include steps. ```TypeScript import { accountSteps } from './account'; // import { accessSteps } from './access'; // This import is removed in the diff // const integrationSteps = [...accountSteps, ...accessSteps]; // Original line const integrationSteps = [...accountSteps]; // Modified line to exclude accessSteps export { integrationSteps }; ``` -------------------------------- ### Initial executionHandler for Fetch Account Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md The initial implementation of an execution handler function for fetching account details. It demonstrates adding an entity and setting job state data. ```ts export async function fetchAccountDetails({ jobState, }: IntegrationStepExecutionContext) { const accountEntity = await jobState.addEntity(createAccountEntity()); await jobState.setData(ACCOUNT_ENTITY_KEY, accountEntity); } ``` -------------------------------- ### Install SDK Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-http-client/README.md Installs the necessary packages for using the JupiterOne Integration SDK HTTP Client, including node-fetch for making HTTP requests. ```bash npm install node-fetch @jupiterone/integration-sdk-http-client ``` -------------------------------- ### Integration Invocation Configuration Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Defines the main invocation configuration for an integration, exporting key components like instance configuration fields, validation logic, and integration steps. ```ts export const invocationConfig: IntegrationInvocationConfig = { instanceConfigFields, validateInvocation, integrationSteps, }; ``` -------------------------------- ### Run Neo4j Docker Container Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-cli/src/neo4j/README.md Starts a Neo4j Docker container with specified volume mounts and environment variables for local Neo4j instance setup. This command configures ports, data persistence, and authentication. ```bash docker run \ -p 7474:7474 -p 7687:7687 \ -d \ -v $PWD/.neo4j/data:/data \ -v $PWD/.neo4j/logs:/logs \ -v $PWD/.neo4j/import:/var/lib/neo4j/import \ -v $PWD/.neo4j/plugins:/plugins \ --env NEO4J_AUTH=neo4j/devpass \ neo4j:latest ``` -------------------------------- ### Install @jupiterone/integration-sdk-core Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-core/README.md Provides commands to install the @jupiterone/integration-sdk-core package using either npm or yarn package managers. ```bash npm install @jupiterone/integration-sdk-core ``` ```bash yarn add @jupiterone/integration-sdk-core ``` -------------------------------- ### Install and Build SDK Source: https://github.com/jupiterone/sdk/blob/main/README.md Commands to install project dependencies and build the TypeScript packages. The `--watch` flag enables incremental compilation for faster development cycles. ```shell npm install npm run build npm run build -- --watch ``` -------------------------------- ### Final executionHandler with Converter Integration Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md The completed execution handler for fetching DigitalOcean account details, now integrating the `createAccountEntity` converter to add the normalized entity to the job state. ```ts import { createAccountEntity } from './converter'; ... export async function fetchAccountDetails({ instance, jobState, logger }: IntegrationStepExecutionContext) { const client = createAPIClient(instance.config, logger); const account = client.getAccount(); const accountEntity = createAccountEntity(account); await jobState.addEntity(accountEntity); } ``` -------------------------------- ### Create DigitalOcean API Client Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Sets up the basic `APIClient` class for DigitalOcean. The constructor is updated to accept `IntegrationLogger` and the `BASE_URL` is defined. The `createAPIClient` factory function is modified to pass the logger to the client instance. ```typescript import { IntegrationConfig, IntegrationLogger } from '@jupiterone/integration-sdk'; export class APIClient { private BASE_URL = 'https://api.digitalocean.com/v2'; constructor( readonly config: IntegrationConfig, readonly logger: IntegrationLogger ) {} // ... other methods } export function createAPIClient( config: IntegrationConfig, logger: IntegrationLogger ): APIClient { return new APIClient(config, logger); } ``` -------------------------------- ### Example Summary File Content (.j1-integration/summary.json) Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development.md Shows the content of the `.j1-integration/summary.json` file, which includes integration step results and metadata for partial datasets. This file is written to disk after the collection phase. ```json { "integrationStepResults": [ { "id": "step-fetch-accounts", "name": "Fetch Accounts", "declaredTypes": ["my_integration_account"], "encounteredTypes": ["my_integration_account"], "status": "success" }, { "id": "step-fetch-users", "name": "Fetch Users", "declaredTypes": ["my_integration_user"], "encounteredTypes": [], "status": "failure" }, { "id": "step-fetch-groups", "name": "Fetch Groups", "declaredTypes": ["my_integration_group"], "encounteredTypes": ["my_integration_group"], "status": "success" }, { "id": "step-build-user-to-group-relationships", "name": "Fetch Accounts", "declaredTypes": ["my_integration_user_to_group_relationship"], "encounteredTypes": ["my_integration_user_to_group_relationship"], "dependsOn": ["step-fetch-users", "step-fetch-groups"], "status": "partial_success_from_dependency_failure" } ], "metadata": { "partialDatasets": { "types": [ "my_integration_user", "my_integration_user_to_group_relationship" ] } } } ``` -------------------------------- ### Token Bucket Examples Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-cli/src/bocchi/docs/template/README.md Illustrates how to configure the `tokenBucket` object in the JSON template for rate limiting. Examples show setting capacity and refill rate for API calls per second. ```json { "maximumCapacity": 10, "refillRate": 10 } ``` ```json { "maximumCapacity": 50, "refillRate": 10 } ``` -------------------------------- ### Instance Configuration Fields Example Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-cli/src/bocchi/docs/template/README.md Provides an example of how to structure the `instanceConfigFields` object within the JSON template, demonstrating different field types and properties like 'optional' and 'mask'. ```json { "accessKey": { "type": "string" }, "accessKeyId": { "type": "string", "optional": false }, "example": { "type": "boolean", "mask": true, "optional": true } } ``` -------------------------------- ### Setup Recording with Polly.js Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/testing.md The `setupRecording` function simplifies the process of recording real API responses using Polly.js for integration tests. It manages the creation of a `__recordings__` directory, allows for header redaction, and accepts Polly.js configuration options for fine-grained control over request matching and recording behavior. ```typescript import step from './my-step'; import provider from './myProvider'; import { Recording, setupRecording } from '@jupiterone/integration-sdk-testing'; let recording: Recording; afterEach(async () => { await recording.stop(); }); test('should generate the expected entities and relationships', () => { recording = setupRecording({ name: 'my awesome recording', directory: __dirname, redactedRequestHeaders: ['api-secret-key'], options: { recordFailedRequests: true, matchRequestsBy: { url: { query: false, }, }, }, }); const resources = []; await provider.iterateThings((e) => { resources.push(e); }); expect(resources).toEqual([ { id: expect.any(String), name: 'development', tags: expect.objectContaining({ someTag: 'value', }), }, ]); }); ``` -------------------------------- ### Install @jupiterone/integration-sdk-runtime Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-runtime/README.md Instructions for installing the integration SDK runtime package using npm or yarn. This package is essential for executing integrations within the JupiterOne environment. ```shell npm install @jupiterone/integration-sdk-runtime ``` ```shell yarn add @jupiterone/integration-sdk-runtime ``` -------------------------------- ### Install @jupiterone/integration-sdk-cli Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-cli/README.md Installs the JupiterOne SDK CLI package using either npm or yarn. This is the first step to using the CLI tool for integration development. ```bash npm install @jupiterone/integration-sdk-cli ``` ```bash yarn add @jupiterone/integration-sdk-cli ``` -------------------------------- ### Instance Configuration Fields Example Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/testing.md An example JSON structure defining fields for an integration's instance configuration, specifying the expected type for each configuration parameter. ```json { "apiKey": { "type": "string" } } ``` -------------------------------- ### API Client for DigitalOcean Account Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Implements an `APIClient` class with a `getAccount` method to fetch account details from the DigitalOcean API. Includes error handling for authentication, authorization, and general API errors. ```TypeScript import { IntegrationLogger, IntegrationProviderAPIError, IntegrationProviderAuthenticationError, IntegrationProviderAuthorizationError, IntegrationValidationError } from '@jupiterone/integration-sdk-core'; import fetch from 'node-fetch'; import { DigitalOceanAccount } from './types'; // Assume BASE_URL and config are defined within the class context // For example: // private readonly BASE_URL: string; // private readonly config: { accessToken: string }; // private logger: IntegrationLogger; export class APIClient { // Placeholder for constructor and other members constructor(private config: any, private logger: IntegrationLogger) { // Initialize BASE_URL, logger, etc. this.BASE_URL = 'https://api.digitalocean.com/v2'; // Example base URL } public async getAccount(): Promise { const endpoint = '/account'; const response = await fetch(this.BASE_URL + endpoint, { headers: { Authorization: `Bearer ${this.config.accessToken}`, }, }); // If the response is not ok, we should handle the error if (!response.ok) { await this.handleApiError(response, this.BASE_URL + endpoint); } return (await response.json()) as DigitalOceanAccount; } private async handleApiError(response: any, endpoint: string): Promise { const errorBody = await response.json().catch(() => ({})); // Attempt to get JSON body for more details const status = response.status; const statusText = response.statusText; if (status === 401) { throw new IntegrationProviderAuthenticationError({ endpoint: endpoint, status: status, statusText: statusText, details: errorBody }); } else if (status === 403) { throw new IntegrationProviderAuthorizationError({ endpoint: endpoint, status: status, statusText: statusText, details: errorBody }); } else { throw new IntegrationProviderAPIError({ endpoint: endpoint, status: status, statusText: statusText, details: errorBody }); } } } // Example of how createAPIClient might be used (context from validateInvocation) // function createAPIClient(config: any, logger: IntegrationLogger) { // return new APIClient(config, logger); // } ``` -------------------------------- ### Install Package Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-entities/README.md Install the `@jupiterone/integration-sdk-entities` package using either npm or yarn package managers. ```bash npm install @jupiterone/integration-sdk-entities ``` ```bash yarn add @jupiterone/integration-sdk-entities ``` -------------------------------- ### Install @jupiterone/integration-sdk-dev-tools Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-dev-tools/README.md Installs the development tools package as a dev dependency using either npm or yarn. This command should be run in the root of your integration project. ```shell npm install -D @jupiterone/integration-sdk-dev-tools # or yarn add -D @jupiterone/integration-sdk-dev-tools ``` -------------------------------- ### Example Integration Step Results Summary Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development.md Illustrates the structure of step results collected during an integration run, showing status, declared types, and dependencies. This format is used to report the outcome of individual integration steps. ```json [ { "id": "step-fetch-accounts", "name": "Fetch Accounts", "declaredTypes": ["my_integration_account"], "encounteredTypes": ["my_integration_account"], "status": "success" }, { "id": "step-fetch-users", "name": "Fetch Users", "declaredTypes": ["my_integration_user"], "encounteredTypes": [], "status": "failure" }, { "id": "step-fetch-groups", "name": "Fetch Groups", "declaredTypes": ["my_integration_group"], "encounteredTypes": ["my_integration_group"], "status": "success" }, { "id": "step-build-user-to-group-relationships", "name": "Fetch Accounts", "declaredTypes": ["my_integration_user_to_group_relationship"], "encounteredTypes": ["my_integration_user_to_group_relationship"], "dependsOn": ["step-fetch-users", "step-fetch-groups"], "status": "partial_success_from_dependency_failure" } ] ``` -------------------------------- ### Define Account Integration Step in TypeScript Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Demonstrates the creation of an 'Account' integration step using TypeScript. It shows how to define the step's unique ID, human-readable name, associated entities, and the execution handler function. This is a core component for building custom integrations within the JupiterOne platform. ```ts export const accountSteps: IntegrationStep[] = [ { id: Steps.ACCOUNT, name: 'Fetch Account Details', entities: [Entities.ACCOUNT], relationships: [], dependsOn: [], executionHandler: fetchAccountDetails, }, ]; ``` -------------------------------- ### Update validateInvocation for Authentication Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Modifies the `validateInvocation` function to create an `APIClient` instance and call `getAccount` to verify authentication with the DigitalOcean API. ```TypeScript import { IntegrationExecutionContext, IntegrationConfig, IntegrationValidationError } from '@jupiterone/integration-sdk-core'; // Assume APIClient and createAPIClient are imported correctly // import { APIClient } from './apiClient'; // declare function createAPIClient(config: any, logger: IntegrationLogger): APIClient; export async function validateInvocation( context: IntegrationExecutionContext ) { const { config } = context.instance; if (!config.accessToken) { throw new IntegrationValidationError( 'Config requires accessToken' ); } // const apiClient = createAPIClient(config); // await apiClient.verifyAuthentication(); // Original commented out line const apiClient = createAPIClient(config, context.logger); await apiClient.getAccount(); // New line to verify authentication } ``` -------------------------------- ### Update Integration Configuration Interface (TypeScript Diff) Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Modifies the `IntegrationConfig` interface to align with the defined `instanceConfigFields`. This ensures type safety and consistency throughout the integration project. ```diff export interface IntegrationConfig extends IntegrationInstanceConfig { /** * The provider API client ID used to authenticate requests. */ clientId: string; /** * The provider API client secret used to authenticate requests. */ clientSecret: string; } // Updated version: export interface IntegrationConfig extends IntegrationInstanceConfig { /** * The accessToken to use when authenticating with the API. */ accessToken: string; } ``` -------------------------------- ### DigitalOcean executionHandler Adaptation Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Adapts the execution handler for DigitalOcean by introducing an API client and fetching account data. This snippet shows the modified function signature and core logic. ```ts export async function fetchAccountDetails({ instance, jobState, logger }: IntegrationStepExecutionContext) { const client = createAPIClient(instance.config, logger); const account = await client.getAccount(); } ``` -------------------------------- ### Define DigitalOceanAccount Type Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Defines the TypeScript interface for the DigitalOcean account response object, modeled from the DigitalOcean API documentation. This interface specifies the structure of data returned by the `/account` endpoint. ```TypeScript // modeled from the example response from the DigitalOcean API // See: https://docs.digitalocean.com/reference/api/api-reference/#tag/Account export interface DigitalOceanAccount { account: { droplet_limit: number; floating_ip_limit: number; email: string; uuid: string; email_verified: boolean; status: string; status_message: string; }; } ``` -------------------------------- ### Account Entity Converter Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md A converter function to transform raw DigitalOcean account data into a normalized JupiterOne entity format. It defines the structure for entity data, including source and assignable properties. ```ts import { createIntegrationEntity, Entity, } from '@jupiterone/integration-sdk-core'; import { DigitalOceanAccount } from '../../types'; import { Entities } from '../constants'; export function createAccountEntity(account: DigitalOceanAccount): Entity { return createIntegrationEntity({ entityData: { source: account, assign: { _key: account.account.uuid, _type: Entities.ACCOUNT._type, _class: Entities.ACCOUNT._class, name: 'Account', dropletLimit: account.account.droplet_limit, floatingIpLimit: account.account.floating_ip_limit, uuid: account.account.uuid, email: account.account.email, emailVerified: account.account.email_verified, status: account.account.status, statusMessage: account.account.status_message, }, }, }); } ``` -------------------------------- ### Define Instance Configuration Fields (TypeScript) Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Defines the configuration fields required for an integration instance, specifying their types and sensitivity. The `mask: true` property is used for sensitive fields like secrets or tokens. ```typescript export const instanceConfigFields: IntegrationInstanceConfigFieldMap = { clientId: { type: 'string', }, clientSecret: { type: 'string', mask: true, }, }; ``` -------------------------------- ### Validate DigitalOcean Invocation Configuration Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Implements the `validateInvocation` function for a DigitalOcean integration. It checks for the presence of the `accessToken` in the configuration and throws an `IntegrationValidationError` if it's missing. Includes commented-out lines for future API client authentication verification. ```typescript import { IntegrationExecutionContext, IntegrationValidationError } from '@jupiterone/integration-sdk'; interface IntegrationConfig { accessToken?: string; } export async function validateInvocation( context: IntegrationExecutionContext ) { const { config } = context.instance; if (!config.accessToken) { throw new IntegrationValidationError( 'Config requires accessToken', ); } // const apiClient = createAPIClient(config); // await apiClient.verifyAuthentication(); } ``` -------------------------------- ### Define Step ID Constants in TypeScript Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md Illustrates the definition of constants for integration step identifiers in TypeScript. This practice ensures consistency and maintainability by centralizing step IDs, which are crucial for referencing steps across different parts of an integration project. ```ts export const Steps = { ACCOUNT: 'fetch-account', USERS: 'fetch-users', GROUPS: 'fetch-groups', GROUP_USER_RELATIONSHIPS: 'build-user-group-relationships', }; ``` -------------------------------- ### Update ACCOUNT Entity Schema for DigitalOcean Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md This diff shows how to modify the `ACCOUNT` entity metadata to conform to a DigitalOcean account structure. It updates the `_type` and changes the `schema` properties and `required` fields to reflect DigitalOcean-specific attributes like email and status. ```diff ACCOUNT: { resourceName: 'Account', - _type: 'acme_account', + _type: 'digital_ocean_account', _class: ['Account'], schema: { properties: { - mfaEnabled: { type: 'boolean' }, - manager: { type: 'string' }, + email: { type: 'string' }, + emailVerified: { type: 'boolean' } + status: { type: 'string' } + statusMessage: { type: 'string' } }, - required: ['mfaEnabled', 'manager'], + required: ['email', 'emailVerified', 'status'] }, }, ``` -------------------------------- ### Version and Release SDK Source: https://github.com/jupiterone/sdk/blob/main/README.md Commands to version all packages within the project and tag the repository. This process involves creating a new branch, pushing it, and then using Lerna to version and tag the commits. ```shell git checkout -b release-.. git push -u origin release-.. npm exec lerna version .. ``` -------------------------------- ### Define ACCOUNT Entity Metadata in TypeScript Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development_guide.md This snippet demonstrates how to define the metadata for an 'ACCOUNT' entity using TypeScript constants. The `StepEntityMetadata` object specifies properties like `resourceName`, `_type`, `_class`, and `schema` to define the structure and classification of data produced by an integration. ```TypeScript ACCOUNT: { resourceName: 'Account', _type: 'acme_account', _class: ['Account'], schema: { properties: { mfaEnabled: { type: 'boolean' }, manager: { type: 'string' }, }, required: ['mfaEnabled', 'manager'], }, }, ``` -------------------------------- ### BaseAPIClient Constructor Options Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-http-client/README.md Details the configuration options available when initializing the BaseAPIClient, covering API base URL, logging, retry strategies, rate limiting, and authentication. ```APIDOC BaseAPIClient: __constructor__(options) Parameters: baseUrl (string, required): The base URL for API endpoints. Requests use this unless a full URL is provided. logger (IntegrationLogger, required): An instance of IntegrationLogger for logging messages. retryOptions (Partial, optional): Configuration for request retries. maxAttempts (number): Maximum number of retry attempts. delay (number): Initial delay between retries in milliseconds. timeout (number): Maximum time to wait for a response before timing out (ms). factor (number): Factor for exponential backoff delay increase. handleError (function): Custom function invoked on error for advanced handling. logErrorBody (boolean, optional): Whether to log the response body on error. Defaults to false. rateLimitThrottling (RateLimitThrottlingOptions, optional): Configuration for API rate limit handling. threshold (number): Percentage (0-1) of rate limit utilization to start throttling. resetMode (string): How to interpret rate limit reset header ('remaining_epoch_s' or 'datetime_epoch_s'). Defaults to 'remaining_epoch_s'. rateLimitHeaders (object): Custom header names for rate limits. limit (string): Header name for the total limit. remaining (string): Header name for remaining requests. reset (string): Header name for reset time. tokenBucket (TokenBucketOptions, optional): Configuration for token bucket rate limiting. maximumCapacity (number): Maximum number of tokens the bucket can hold. refillRate (number): Rate at which tokens are added per second. ``` -------------------------------- ### CLI Integration and Future CLI Tools Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development.md Discusses the integration of the `j1-integration` CLI into the core `j1` CLI and the future development of a unified `@jupiterone/dev-tools` project for all JupiterOne development tasks, including queries, questions, and rules. ```bash One CLI tool for all JupiterOne development The `j1-integration` CLI is a standalone tool designed to work just for integration development. The CLI tool will be designed in a way that allows for it to be added as an `integration` subcommand for the `j1` CLI (so you can run things like `j1 integration sync` but also maybe something like `j1 query` from a single tool). This will likely be done by forwarding commands from the core `j1` cli to the `j1-integration` executable or by exposing the code used for constructing the `j1-integration` CLI to the project that handles the `j1` cli. In the future, the `j1` CLI will provide a suite of commands for interfacing with queries, questions, rules, and various other JupiterOne features. This may end up living under a `@jupiterone/dev-tools` project. That repo might even end up becoming a monorepo and become a one-stop shop for all JupiterOne related development tools. ``` -------------------------------- ### Run SDK Benchmarks Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-benchmark/README.md Executes the SDK benchmark suite using npm. This command is used to measure the performance of the SDK and should be run before and after making performance-related changes. ```shell npm run benchmark ``` -------------------------------- ### API Response Configuration Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-cli/src/bocchi/docs/template/steps.md Defines how to process the API response, including locating data, determining response type, and extracting pagination tokens. ```APIDOC response: Type: Object Required: true Properties: dataPath: Type: String Required: true Description: The JSON path within the API response to iterate over for entities. Example: API Response: { "response": { "data": [ { "..." }, { "..." }, { "..." }, "..." ] } } Template: { "dataPath": "response.data" } responseType: Type: String Required: true Description: Specifies whether the response contains a single entity ('SINGLETON') or multiple entities ('LIST'). Examples: - SINGLETON: For responses like { "data": { "bocchi": "the rock" } } - LIST: For responses like { "data": [{ "bocchi": "the rock" }, { "japan": "is nice" }] } nextTokenPath: Type: String Required: false Description: The JSON path in the API response to the next token for pagination. Example: API Response: { "response": { "data": ["..."], "pagination": { "limit": 100, "cursor": "abcd1234" } } } Template: { "nextTokenPath": "response.pagination.cursor" } Example: { "dataPath": "data", "responseType": "LIST", "nextTokenPath": "next" } ``` -------------------------------- ### Configure Husky Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-dev-tools/README.md Sets up Husky for Git hooks, ensuring code quality checks before commits. Create a `husky.config.js` file in your project root to integrate Husky with the provided configuration. ```javascript module.exports = require('@jupiterone/integration-sdk-dev-tools/config/husky'); ``` -------------------------------- ### j1-integration sync command options Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development.md Describes optional parameters for the 'j1-integration sync' command, including API key, tailing job status, and specifying an API base URL. These options control authentication, real-time job monitoring, and endpoint configuration. ```APIDOC Option --source Specifies the bulk upload job `source` property. This is not required when `--integrationInstanceId` is specified. `--scope` is required when `--source` is specified. Option --scope Specifies the bulk upload job `scope` property. This is not required when `--integrationInstanceId` is specified. `--source` is required when `--scope` is specified. Option --api-key or -k Like the `collect` command, an API key can be optionally passed in to use for synchronization. Example: `j1-integration sync --integrationInstanceId --api-key ` Option --tail or -t If provided this option poll the integration job to and display the status of the job run. The polling will stop once the job was marked as complete. Example: `j1-integration sync --integrationInstanceId --tail` Option --api-base-url If provided this option specifies which base URL to use for synchronization. Cannot be used with the `--development` flag. Example: `yarn j1-integration sync --integrationInstanceId --api-base-url ` Option --noPretty Disables pretty printing of logs. ``` -------------------------------- ### Step Object Fields Documentation Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-cli/src/bocchi/docs/template/steps.md Detailed documentation for the properties within a JupiterOne SDK step object, covering entity configuration, API request/response details, relationship mapping, and dependency management. ```APIDOC Step Object Fields: - id: - Property: `id` - Type: `String` - Required: `true` - Description: Unique identifier of the step. - Example: `fetch-github-users` - name: - Property: `name` - Type: `String` - Required: `true` - Description: User-facing name of the step. - Example: `Fetch GitHub Users` - entity: - Property: `entity` - Type: `Object` - Required: `true` - Description: The type entity that will be ingested for this step. - Fields: - name: - Property: `name` - Type: `String` - Required: `true` - Description: Name of the entity. - Example: `Cloud WAF Instance` - _type: - Property: `_type` - Type: `String` - Required: `true` - Description: Type of the entity. - Example: `github_user` - _class: - Property: `_class` - Type: `String` | `String[]` - Required: `true` - Description: Class(es) of the entity. - Example: `User` - _keyPath: - Property: `_keyPath` - Type: `String` - Required: `true` - Description: The property path in the entity that should serve as the entity's _key. Do not use templating. - Example: `email` - staticFields: - Property: `staticFields` - Type: `Object` - Required: `false` - Description: Properties that will appear on all generated entities in the step. - Example: `{ bocchi: "the rock" shiba: "inu" }` - fieldMappings: - Property: `fieldMappings` - Type: `Object` - Required: `false` - Description: Optionally specify mappings to convert data to entity properties using dot notation for nested properties. - Example: Original data: ```json { "id": 1, "foo": { "bar": 2, "baz": ["123"] } } ``` fieldMappings: ```json { "id": "id", "name": "foo.bar", "groups": "foo.baz" } ``` Resulting entity: ```json { "id": 1, "name": 2, "groups": ["123"] } ``` - parentRelationship: - Property: `parentRelationship` - Type: `Object` - Required: `false` - Fields: - _class: - Property: `_class` - Type: `RelationshipClass` - Required: `true` - Description: The class of the parent relationship. - request: - Property: `request` - Type: `Object` - Required: `true` - Description: Configuration for the API request to fetch data. - Fields: - urlTemplate: - Property: `urlTemplate` - Type: `String` - Required: `true` - Description: The URL template for the API request. - method: - Property: `method` - Type: `'GET' | 'POST'` - Required: `false` - Description: The HTTP method for the API request. - params: - Property: `params` - Type: `any` - Required: `false` - Description: Parameters for the API request. - response: - Property: `response` - Type: `Object` - Required: `true` - Description: Configuration for processing the API response. - Fields: - dataPath: - Property: `dataPath` - Type: `String` - Required: `true` - Description: The path within the response JSON to extract the main data. - responseType: - Property: `responseType` - Type: `String` - Required: `true` - Description: The type of the response data (e.g., 'json'). - nextTokenPath: - Property: `nextTokenPath` - Type: `String` - Required: `false` - Description: The path within the response JSON to find the token for pagination. - directRelationships: - Property: `directRelationships` - Type: `Array` - Required: `false` - Description: Defines direct relationships to other entities. - Fields: - targetKey: - Property: `targetKey` - Type: `String` - Required: `true` - Description: The key of the target entity. - targetType: - Property: `targetType` - Type: `String` - Required: `true` - Description: The type of the target entity. - _class: - Property: `_class` - Type: `RelationshipClass` - Required: `true` - Description: The class of the relationship. - direction: - Property: `direction` - Type: `'FORWARD' | 'REVERSE'` - Required: `true` - Description: The direction of the relationship. - mappedRelationships: - Property: `mappedRelationships` - Type: `Array` - Required: `false` - Description: Defines relationships mapped from source properties to target properties or values. - Fields: - _class: - Property: `_class` - Type: `RelationshipClass` - Required: `true` - Description: The class of the relationship. - direction: - Property: `direction` - Type: `'FORWARD' | 'REVERSE'` - Required: `true` - Description: The direction of the relationship. - fieldMappings: - Property: `fieldMappings` - Type: `Object` - Required: `true` - Description: Mappings for creating the relationship. - Structure: - `{ sourceProperty: string, targetProperty: string }` - `{ targetValue: string, targetProperty: string }` - dependsOn: - Property: `dependsOn` - Type: `Array` - Required: `false` - Description: An array of step IDs that this step depends on, ensuring execution order. ``` -------------------------------- ### Create JupiterOne Database Entity Example Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development.md Example demonstrating the usage of `createIntegrationEntity` to convert Azure SQL database data into a JupiterOne entity, including mapping properties and assigning core attributes. ```typescript export function createDatabaseEntity( webLinker: AzureWebLinker, data: MySQLDatabase | SQLDatabase, _type: string, ) { return createIntegrationEntity({ entityData: { source: data, assign: { ...convertProperties(data), _type, _class: AZURE_DATABASE_ENTITY_CLASS, displayName: data.name || data.id || 'unnamed', classification: null, encrypted: null, }, }, }); } ``` -------------------------------- ### j1-integration collect Command Usage Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development.md Explains how to use the `j1-integration collect` command to run the JavaScript framework locally for data collection. It outlines the search order for integration configuration files and the output directory. ```shell j1-integration collect This command will run the js framework locally to _only_ perform data collection. The `collect` command will look for an integration configuration from files in the following order relative to the current working directory: 0. `index.js` 1. `index.ts` 2. `src/index.js` 3. `src/index.ts` Data will be written to disk under a generated `.j1-integration` directory. ``` -------------------------------- ### JupiterOne Integration SDK Testing Utilities Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/testing.md Provides utilities to simplify unit testing of JupiterOne integrations, including mocking execution contexts and setting up recordings for client testing. ```APIDOC JupiterOneIntegrationSDKTesting: createMockExecutionContext(): object Description: Creates a mock execution context for testing integration steps. Usage: Useful for isolating and testing individual integration steps without running a full integration. executeStepWithDependencies(step: Function, dependencies: object, context: object): Promise Description: Executes an integration step with provided dependencies and context. Parameters: - step: The integration step function to execute. - dependencies: An object containing any dependencies the step requires. - context: The execution context for the step. Returns: A promise that resolves with the result of the step execution. Usage: Facilitates testing of steps that rely on other components or data. setupRecording(options?: object): object Description: Sets up recording for testing client code, often used with HTTP-based data sources via Polly.js. Parameters: - options: Optional configuration for the recording setup. Returns: An object or interface for managing recordings. Usage: Enables recording and playback of API responses for robust client testing, including error scenarios. ``` -------------------------------- ### Configure ESLint Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-dev-tools/README.md Configures ESLint for code linting, ensuring code style and quality. Create a `.eslintrc` file in your project root to apply the package's ESLint configuration. ```json { "root": true, "extends": [ "./node_modules/@jupiterone/integration-sdk-dev-tools/config/eslint.json" ] } ``` -------------------------------- ### Generate TypeScript Types Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-entities/README.md To generate TypeScript types, ensure `json-schema-to-typescript` and `ts-dedupe` are installed. Then, run the `npm run generate-ts-classes` command. This process reads schemas directly from `node_modules`, so verify the data model installation. ```json { "json-schema-to-typescript": "^12.0.0", "ts-dedupe": "^0.3.1" } ``` ```bash npm run generate-ts-classes ``` -------------------------------- ### Create Direct Relationship Example (Literal) Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development.md Demonstrates using `createDirectRelationship` with `DirectRelationshipLiteralOptions` to link entities by their type and key identifiers. ```typescript // usage createDirectRelationship({ _class: RelationshipClass.HAS, fromKey: 'a', fromType: 'a_entity', toKey: 'b', toType: 'b_entity', }); ``` -------------------------------- ### Run JupiterOne Collect with Specific Steps Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development.md Execute specific integration steps and their dependencies using the `--step` option. This can improve performance by leveraging the Step Cache for dependent steps. Multiple steps can be specified individually or as a comma-delimited list. ```CLI j1-integration collect --step step-fetch-users --step step-fetch-groups ``` ```CLI j1-integration collect --step step-fetch-users,step-fetch-groups ``` -------------------------------- ### Create Direct Relationship Example (Direct) Source: https://github.com/jupiterone/sdk/blob/main/docs/integrations/development.md Illustrates using `createDirectRelationship` with `DirectRelationshipOptions` to link two entities (`entityA`, `entityB`) by providing their direct object references. ```typescript // usage createDirectRelationship({ _class: RelationshipClass.HAS, source: entityA, target: entityB, }); ``` -------------------------------- ### Configure Prettier Source: https://github.com/jupiterone/sdk/blob/main/packages/integration-sdk-dev-tools/README.md Sets up Prettier for code formatting by exporting the package's Prettier configuration. Create a `prettier.config.js` file in your project root to use this configuration. ```javascript module.exports = require('@jupiterone/integration-sdk-dev-tools/config/prettier'); ```