From da1850b7e98ff129f63cfcc2a726301e87a95dc4 Mon Sep 17 00:00:00 2001 From: radovan-jorgic <147607814+radovan-jorgic@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:43:02 +0200 Subject: [PATCH] Support for new recipe management (#4) Started supporting new recipe management which includes creating starting recipe blueprint and installing initial domain mapping file on snap-in activation. Added example of external domain metadata file and changes in demo extractor. --- README.md | 4 + package.json | 2 +- src/common/install-initial-domain-mapping.ts | 93 +++++++++++++++ .../external_domain_metadata.json | 38 +++++++ src/demo-extractor/index.ts | 80 ++++++------- .../initial_domain_mapping.json | 107 ------------------ src/index.ts | 2 + src/types/common.ts | 5 + src/types/extraction.ts | 1 + tests/adapter.test.ts | 1 + tests/demo-extractor.test.ts | 1 + tests/state.test.ts | 1 + 12 files changed, 182 insertions(+), 153 deletions(-) create mode 100644 src/common/install-initial-domain-mapping.ts create mode 100644 src/demo-extractor/external_domain_metadata.json delete mode 100644 src/demo-extractor/initial_domain_mapping.json diff --git a/README.md b/README.md index 7a2e7eb..53583da 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ Typescript ADaaS Library (@devrev/ts-adaas) provides: ## Release Notes +#### v0.0.3 + +- Support for new recipe management + #### v0.0.2 - Support for the State API diff --git a/package.json b/package.json index bdf80c7..2579bde 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devrev/ts-adaas", - "version": "0.0.2", + "version": "0.0.3", "description": "Typescript library containing the ADaaS(AirDrop as a Service) control protocol.", "type": "commonjs", "main": "./dist/src/index.js", diff --git a/src/common/install-initial-domain-mapping.ts b/src/common/install-initial-domain-mapping.ts new file mode 100644 index 0000000..cd590b5 --- /dev/null +++ b/src/common/install-initial-domain-mapping.ts @@ -0,0 +1,93 @@ +import axios from 'axios'; +import { AirdropEvent } from '../types/extraction'; +import { InitialDomainMapping } from '../types/common'; + +export async function installInitialDomainMapping( + event: AirdropEvent, + initialDomainMappingJson: InitialDomainMapping +) { + const devrevEndpoint = event.execution_metadata.devrev_endpoint; + const devrevToken = event.context.secrets.service_account_token; + const snapInVersionId = event.context.snap_in_version_id; + + if (!initialDomainMappingJson) { + console.warn('No initial domain mapping found'); + return; + } + + const snapInVersionResponse = await axios.get( + devrevEndpoint + '/internal/snap-in-versions.get', + { + headers: { + Authorization: devrevToken, + }, + params: { + id: snapInVersionId, + }, + } + ); + + const importSlug = snapInVersionResponse.data.snap_in_version.imports[0].slug; + const snapInSlug = snapInVersionResponse.data.snap_in_version.slug; + const startingRecipeBlueprint = + initialDomainMappingJson?.starting_recipe_blueprint; + + let recipeBlueprintId; + if ( + startingRecipeBlueprint && + Object.keys(startingRecipeBlueprint).length !== 0 + ) { + try { + const recipeBlueprintResponse = await axios.post( + `${devrevEndpoint}/internal/airdrop.recipe.blueprints.create`, + { + ...startingRecipeBlueprint, + }, + { + headers: { + Authorization: devrevToken, + }, + } + ); + + recipeBlueprintId = recipeBlueprintResponse.data.recipe_blueprint.id; + + console.log( + 'Successfully created recipe blueprint with id: ' + recipeBlueprintId + ); + } catch (error) { + console.error('Error while creating recipe blueprint', error); + } + } + + try { + // 2. Install the initial domain mappings + const additionalMappings = + initialDomainMappingJson.additional_mappings || {}; + const initialDomainMappingInstallResponse = await axios.post( + `${devrevEndpoint}/internal/airdrop.recipe.initial-domain-mappings.install`, + { + external_system_type: 'ADaaS', + import_slug: importSlug, + snap_in_slug: snapInSlug, + ...(recipeBlueprintId && { + starting_recipe_blueprint: recipeBlueprintId, + }), + ...additionalMappings, + }, + { + headers: { + Authorization: devrevToken, + }, + } + ); + + console.log( + 'Successfully installed initial domain mapping', + initialDomainMappingInstallResponse.data + ); + } catch (error) { + console.error('Error while installing initial domain mapping', error); + return; + } +} diff --git a/src/demo-extractor/external_domain_metadata.json b/src/demo-extractor/external_domain_metadata.json new file mode 100644 index 0000000..ea87648 --- /dev/null +++ b/src/demo-extractor/external_domain_metadata.json @@ -0,0 +1,38 @@ +{ + "record_types": { + "users": { + "fields": { + "name": { + "is_required": true, + "type": "text", + "name": "Name", + "text": { + "min_length": 1 + } + }, + "email": { + "type": "text", + "name": "Email", + "is_required": true + } + } + }, + "contacts": { + "fields": { + "name": { + "is_required": true, + "type": "text", + "name": "Name", + "text": { + "min_length": 1 + } + }, + "email": { + "type": "text", + "name": "Email", + "is_required": true + } + } + } + } +} diff --git a/src/demo-extractor/index.ts b/src/demo-extractor/index.ts index 2927816..0bdbeed 100644 --- a/src/demo-extractor/index.ts +++ b/src/demo-extractor/index.ts @@ -8,7 +8,7 @@ import { import { Adapter } from '../adapter'; import { Uploader } from '../uploader'; -import extractorInitialDomainMapping from './initial_domain_mapping.json'; +import externalDomainMetadata from './external_domain_metadata.json'; type ExtractorState = object; @@ -48,21 +48,10 @@ export class DemoExtractor { } case EventType.ExtractionMetadataStart: { - const metadata = [ - { - item: 'contacts', - fields: ['id', 'name', 'lastName'], - }, - { - item: 'users', - fields: ['id', 'name', 'lastName'], - }, - ]; - const { artifact, error } = await this.uploader.upload( - 'loopback_metadata_1.jsonl', - 'metadata', - metadata + 'metadata_1.jsonl', + 'external_domain_metadata', + externalDomainMetadata ); if (error || !artifact) { @@ -72,23 +61,8 @@ export class DemoExtractor { return; } - const { artifact: recipe, error: recipeError } = - await this.uploader.upload( - 'recipe.json', - 'initial_domain_mapping', - extractorInitialDomainMapping - ); - - if (recipeError || !recipe) { - await this.adapter.emit(ExtractorEventType.ExtractionMetadataError, { - error: recipeError, - }); - return; - } - await this.adapter.emit(ExtractorEventType.ExtractionMetadataDone, { - progress: 50, - artifacts: [artifact, recipe], + artifacts: [artifact], }); break; @@ -97,19 +71,27 @@ export class DemoExtractor { case EventType.ExtractionDataStart: { const contacts = [ { - id: 1, - name: 'John', - lastName: 'Doe', + id: 'contact-1', + created_date: '1999-12-25T01:00:03+01:00', + modified_date: '1999-12-25T01:00:03+01:00', + data: { + email: 'johnsmith@test.com', + name: 'John Smith', + }, }, { - id: 2, - name: 'Jane', - lastName: 'Doe', + id: 'contact-2', + created_date: '1999-12-27T15:31:34+01:00', + modified_date: '2002-04-09T01:55:31+02:00', + data: { + email: 'janesmith@test.com', + name: 'Jane Smith', + }, }, ]; const { artifact, error } = await this.uploader.upload( - 'loopback_contacts_1.json', + 'contacts_1.json', 'contacts', contacts ); @@ -133,19 +115,27 @@ export class DemoExtractor { case EventType.ExtractionDataContinue: { const users = [ { - id: 1, - name: 'John', - lastName: 'Phd', + id: 'user-1', + created_date: '1999-12-25T01:00:03+01:00', + modified_date: '1999-12-25T01:00:03+01:00', + data: { + email: 'johndoe@test.com', + name: 'John Doe', + }, }, { - id: 2, - name: 'Jane', - lastName: 'Phd', + id: 'user-2', + created_date: '1999-12-27T15:31:34+01:00', + modified_date: '2002-04-09T01:55:31+02:00', + data: { + email: 'janedoe@test.com', + name: 'Jane Doe', + }, }, ]; const { artifact, error } = await this.uploader.upload( - 'loopback_users_1.json', + 'users_1.json', 'users', users ); diff --git a/src/demo-extractor/initial_domain_mapping.json b/src/demo-extractor/initial_domain_mapping.json deleted file mode 100644 index b0d2c82..0000000 --- a/src/demo-extractor/initial_domain_mapping.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "initial_object_mappings": [ - { - "external_type": "contacts", - "possible_targets": { - "rev_user": { - "initial_field_mappings": [ - { - "destination_field": { - "id": "id", - "is_required": true - }, - "selected_resolutions": ["id"], - "resolutions": { - "id": { - "source_field_description": { - "id": "id", - "is_required": true - } - } - }, - "is_finished": true - }, - { - "destination_field": { - "id": "display_name", - "is_required": true - }, - "selected_resolutions": ["name"], - "resolutions": { - "name": { - "source_field_description": { - "id": "name", - "is_required": true - } - } - }, - "is_finished": true - } - ] - } - }, - "default_target": "rev_user", - "allow_item_type_decisions": true - }, - { - "external_type": "users", - "possible_targets": { - "dev_user": { - "initial_field_mappings": [ - { - "destination_field": { - "id": "id", - "is_required": true - }, - "selected_resolutions": ["id"], - "resolutions": { - "id": { - "source_field_description": { - "id": "id", - "is_required": true - } - } - }, - "is_finished": true - }, - { - "destination_field": { - "id": "display_name", - "is_required": true - }, - "selected_resolutions": ["name"], - "resolutions": { - "name": { - "source_field_description": { - "id": "name", - "is_required": true - } - } - }, - "is_finished": true - } - ] - } - }, - "default_target": "dev_user", - "allow_item_type_decisions": true - }, - { - "external_type": "tickets", - "possible_targets": { - "work.ticket": {} - }, - "default_target": "work.ticket", - "allow_item_type_decisions": true - }, - { - "external_type": "conversations", - "possible_targets": { - "comment": {} - }, - "default_target": "comment", - "allow_item_type_decisions": true - } - ], - "external_system_short_name": "Freshdesk" -} diff --git a/src/index.ts b/src/index.ts index c266947..28b7539 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,3 +3,5 @@ export * from './demo-extractor'; export * from './uploader'; export * from './types'; export * from './http'; + +export * from './common/install-initial-domain-mapping'; diff --git a/src/types/common.ts b/src/types/common.ts index dd01c55..c6888fc 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -37,3 +37,8 @@ export interface UploadResponse { artifact?: Artifact; error?: ErrorRecord; } + +export interface InitialDomainMapping { + starting_recipe_blueprint?: object; + additional_mappings?: object; +} diff --git a/src/types/extraction.ts b/src/types/extraction.ts index 0f36277..4cc7348 100644 --- a/src/types/extraction.ts +++ b/src/types/extraction.ts @@ -150,6 +150,7 @@ export interface AirdropEvent { secrets: { service_account_token: string; }; + snap_in_version_id: string; }; payload: AirdropMessage; execution_metadata: { diff --git a/tests/adapter.test.ts b/tests/adapter.test.ts index 90dd5ca..6bdeb8a 100644 --- a/tests/adapter.test.ts +++ b/tests/adapter.test.ts @@ -32,6 +32,7 @@ describe('Adapter', () => { secrets: { service_account_token: 'service_account_token', }, + snap_in_version_id: 'snap_in_version_id', }, payload: { connection_data: { diff --git a/tests/demo-extractor.test.ts b/tests/demo-extractor.test.ts index 9156ed3..540498d 100644 --- a/tests/demo-extractor.test.ts +++ b/tests/demo-extractor.test.ts @@ -12,6 +12,7 @@ const mockEvent: AirdropEvent = { secrets: { service_account_token: 'service_account_token', }, + snap_in_version_id: 'snap_in_version_id', }, payload: { connection_data: { diff --git a/tests/state.test.ts b/tests/state.test.ts index 75be652..0d2497f 100644 --- a/tests/state.test.ts +++ b/tests/state.test.ts @@ -17,6 +17,7 @@ describe('AdapterState', () => { secrets: { service_account_token: 'service_account_token', }, + snap_in_version_id: 'snap_in_version_id', }, payload: { connection_data: {