From 241c82820dbe1efbb700b48f975cacf036579ebe Mon Sep 17 00:00:00 2001 From: erkangz Date: Thu, 21 May 2020 12:13:35 -0400 Subject: [PATCH] add relationships; use generic types --- docs/jupiterone.md | 12 +++++- src/converter/entities.ts | 35 +++++++++++++--- src/steps/fetch-account/index.ts | 41 +++++++++++++------ .../fetch-hardware/__tests__/index.test.ts | 2 +- src/steps/fetch-hardware/index.ts | 34 +++++++++++++-- 5 files changed, 99 insertions(+), 25 deletions(-) diff --git a/docs/jupiterone.md b/docs/jupiterone.md index bd1cda0..6b30f53 100644 --- a/docs/jupiterone.md +++ b/docs/jupiterone.md @@ -16,8 +16,16 @@ The following entity resources are ingested when the integration runs. | Snipe-IT Resources | \_type of the Entity | \_class of the Entity | | ------------------ | -------------------- | --------------------- | | Account | `snipeit_account` | `Account` | -| Hardware | `snipeit_hardware` | `Device` | -| Location | `snipeit_location` | `Site` | +| Hardware | `hardware` | `Device` | +| Location | `location` | `Site` | + +The following relationships are created: + +| From | Relationship | To | +| ----------------- | ------------ | ---------- | +| `snipeit_account` | **MANAGES** | `location` | +| `snipeit_account` | **MANAGES** | `hardware` | +| `location` | **HAS** | `hardware` | The following relationships are mapped: diff --git a/src/converter/entities.ts b/src/converter/entities.ts index 24cc3e6..a03c7d2 100644 --- a/src/converter/entities.ts +++ b/src/converter/entities.ts @@ -3,8 +3,29 @@ import { createIntegrationEntity, getTime, convertProperties, + Entity, } from '@jupiterone/integration-sdk'; +export const getAccountEntity = (instance: any): Entity => ({ + _key: `snipe-it:account:${instance.id}`, + _type: 'snipeit_account', + _class: ['Account'], + name: instance.name, + displayName: instance.name, + description: instance.description, +}); + +export const getServiceEntity = (instance: any): Entity => ({ + _key: `snipe-it:service:${instance.id}:itam`, + _type: 'snipe_service', + _class: ['Service'], + name: 'Snipe-IT ITAM', + displayName: 'Snipe-IT ITAM', + description: 'IT Asset Management (ITAM)', + category: 'infrastructure', + function: 'ITAM', +}); + export const convertHardware = ( data: any, ): ReturnType => @@ -13,10 +34,10 @@ export const convertHardware = ( source: data, assign: { ...convertProperties(data), - _key: `snipe-it:hardware:${data.id}`, - _type: 'snipeit_hardware', + _key: `hardware:${data.id}`, + _type: 'hardware', _class: ['Device'], - id: `snipe-it:hardware:${data.id}`, + id: `hardware:${data.id}`, assetId: data.id, displayName: data.name, activatedOn: getTime(data.activated_on), @@ -32,6 +53,8 @@ export const convertHardware = ( status: data.status_label?.status_meta === 'deployable' ? 'ready' + : data.status_label?.name?.match(/broken/i) + ? 'defective' : data.status_label?.status_meta, notes: data.notes ? [data.notes] : undefined, location: data.location?.name, @@ -50,10 +73,10 @@ export const convertLocation = ( source: data, assign: { ...convertProperties(data), - _key: `snipe-it:location:${data.id}`, - _type: 'snipeit_location', + _key: `location:${data.id}`, + _type: 'location', _class: ['Site'], - id: `snipe-it:location:${data.id}`, + id: `location:${data.id}`, locationId: data.id, displayName: data.name, createdOn: getTime(data.created_at?.datetime), diff --git a/src/steps/fetch-account/index.ts b/src/steps/fetch-account/index.ts index 5f22cfc..c82b1c7 100644 --- a/src/steps/fetch-account/index.ts +++ b/src/steps/fetch-account/index.ts @@ -1,34 +1,51 @@ import { IntegrationStep, IntegrationStepExecutionContext, + createIntegrationRelationship, } from '@jupiterone/integration-sdk'; import { createServicesClient } from '../../collector'; -import { convertLocation } from '../../converter'; +import { + convertLocation, + getAccountEntity, + getServiceEntity, +} from '../../converter'; const step: IntegrationStep = { id: 'fetch-account', name: 'Fetch Account related data', - types: ['snipeit_account', 'snipeit_location'], + types: ['snipeit_account', 'location'], async executionHandler({ instance, jobState, }: IntegrationStepExecutionContext) { const client = createServicesClient(instance); - const accountEntity = { - _key: `snipe-it:account:${instance.id}`, - _type: 'snipeit_account', - _class: ['Account'], - name: instance.name, - displayName: instance.name, - description: instance.description, - }; + const accountEntity = getAccountEntity(instance); await jobState.addEntity(accountEntity); + const serviceEntity = getServiceEntity(instance); + await jobState.addEntity(serviceEntity); + + const accountServiceRelationship = createIntegrationRelationship({ + from: accountEntity, + to: serviceEntity, + _class: 'PROVIDES', + }); + await jobState.addRelationship(accountServiceRelationship); + const locations = await client.listLocations(); - const entities = locations.map(convertLocation); - await jobState.addEntities(entities); + const locationEntities = locations.map(convertLocation); + await jobState.addEntities(locationEntities); + + const relationships = locationEntities.map((locationEntity) => + createIntegrationRelationship({ + from: accountEntity, + to: locationEntity, + _class: 'MANAGES', + }), + ); + await jobState.addRelationships(relationships); }, }; diff --git a/src/steps/fetch-hardware/__tests__/index.test.ts b/src/steps/fetch-hardware/__tests__/index.test.ts index 924c628..ed26201 100644 --- a/src/steps/fetch-hardware/__tests__/index.test.ts +++ b/src/steps/fetch-hardware/__tests__/index.test.ts @@ -33,7 +33,7 @@ test('should process hardware entities', async () => { expect(context.jobState.collectedEntities).toEqual( expect.arrayContaining([ expect.objectContaining({ - _type: 'snipeit_hardware', + _type: 'hardware', _class: ['Device'], id: expect.any(String), displayName: expect.any(String), diff --git a/src/steps/fetch-hardware/index.ts b/src/steps/fetch-hardware/index.ts index ff2d8b5..5eb29e8 100644 --- a/src/steps/fetch-hardware/index.ts +++ b/src/steps/fetch-hardware/index.ts @@ -1,24 +1,50 @@ import { IntegrationStep, IntegrationStepExecutionContext, + createIntegrationRelationship, } from '@jupiterone/integration-sdk'; import { createServicesClient } from '../../collector'; -import { convertHardware } from '../../converter'; +import { convertHardware, getAccountEntity } from '../../converter'; const step: IntegrationStep = { id: 'fetch-hardware', name: 'Fetch Snipe-IT listing of hardware assets', - types: ['snipeit_hardware'], + types: ['hardware'], async executionHandler({ instance, jobState, }: IntegrationStepExecutionContext) { const client = createServicesClient(instance); + const accountEntity = getAccountEntity(instance); const hardware = await client.listHardware(); - const entities = hardware.map(convertHardware); - await jobState.addEntities(entities); + + const hardwareEntities = hardware.map(convertHardware); + await jobState.addEntities(hardwareEntities); + + const relationships = []; + hardwareEntities.forEach((hardwareEntity) => { + relationships.push( + createIntegrationRelationship({ + from: accountEntity, + to: hardwareEntity, + _class: 'MANAGES', + }), + ); + if (hardwareEntity.locationId) { + relationships.push( + createIntegrationRelationship({ + fromType: 'location', + fromKey: `location:${hardwareEntity.locationId}`, + toType: hardwareEntity._type, + toKey: hardwareEntity._key, + _class: 'HAS', + }), + ); + } + }); + await jobState.addRelationships(relationships); }, };