Development Guide

Object Mappers

Object Mappers are a core component of AirSync that manage and track the relationships between objects imported from external systems and their corresponding entities in DevRev. Think of Object Mappers as a comprehensive lookup table that maintains the synchronization state between external and DevRev objects.

Each sync mapper record contains external system identifiers, corresponding DevRev entity IDs, sync unit scope, operational status, and additional metadata. This bidirectional mapping system enables AirSync to efficiently track which external objects have been synchronized and locate their DevRev counterparts during ongoing operations. Object Mappers are available through the adapter object as adapter.mappers in your AirSync snap-in functions.

When to use Object Mappers

Object Mappers are essential in several AirSync scenarios:

During data extraction and relationship management - Check if sync mapper records already exist to avoid duplicates, find related DevRev entities for nested objects like comments and attachments, and link child objects to their parent entities.

import { SyncMapperRecordTargetType } from "@devrev/ts-adaas";

// Check if sync mapper record for attachment already exists
const existingSyncMapperRecord = await adapter.mappers.getByExternalId({
  sync_unit: adapter.event.payload.event_context.sync_unit,
  external_id: attachment.id,
  target_type: SyncMapperRecordTargetType.ARTIFACT,
});

// Find parent work item for comment
const parentSyncMapperRecord = await adapter.mappers.getByExternalId({
  sync_unit: adapter.event.payload.event_context.sync_unit,
  external_id: externalComment.ticket_id,
  target_type: SyncMapperRecordTargetType.WORK,
});

During loading operations - Locate IDs of objects in external system for DevRev entities when pushing data back to external systems and maintain consistency between systems.

// Find external ID to update external system
const syncMapperRecord = await adapter.mappers.getByTargetId({
  sync_unit: adapter.event.payload.event_context.sync_unit,
  target: devrevWorkId,
});

Object Mapper methods

Get by DevRev ID

Use getByTargetId when you have a DevRev entity ID and need to find the corresponding ID of an object in external system:

const response = await adapter.mappers.getByTargetId({
  sync_unit: adapter.event.payload.event_context.sync_unit,
  target: devrevEntityId,
});

const externalIds = response.data.sync_mapper_record.external_ids;

Get by external ID

Use getByExternalId when you have an ID of an object in external system and need to find the corresponding DevRev entity:

import { SyncMapperRecordTargetType } from "@devrev/ts-adaas";

const response = await adapter.mappers.getByExternalId({
  sync_unit: adapter.event.payload.event_context.sync_unit,
  external_id: externalSystemId,
  target_type: SyncMapperRecordTargetType.WORK,
});

const devrevTargets = response.data.sync_mapper_record.targets;

Create new sync mapper record

Use create to establish a new sync mapper record between external and DevRev entities. Create a mapper only when you are persisting a new link (for example, right after creating the external object in reverse sync), not for read-only extraction. This prevents duplicates in subsequent syncs by enabling lookups in both directions.

import { SyncMapperRecordStatus } from "@devrev/ts-adaas";

const response = await adapter.mappers.create({
  sync_unit: adapter.event.payload.event_context.sync_unit,
  external_ids: [externalSystemId],
  targets: [devrevEntityId],
  status: SyncMapperRecordStatus.OPERATIONAL,
});

Update existing sync mapper record

Use update to modify an existing sync mapper record:

import { SyncMapperRecordStatus } from "@devrev/ts-adaas";

const response = await adapter.mappers.update({
  id: syncMapperRecordId,
  sync_unit: adapter.event.payload.event_context.sync_unit,
  external_ids: {
    add: [newExternalId],
  },
  targets: {
    add: [newDevrevEntityId],
  },
  status: SyncMapperRecordStatus.OPERATIONAL,
});

Sync mapper record status values

StatusValueDescription
OPERATIONAL'operational'The mapping is active and operational (default)
FILTERED'filtered'The mapping was filtered out by user filter settings
IGNORED'ignored'The external object should be ignored in sync operations. Use to prevent objects from being created or updated in DevRev.

Common target types

The SyncMapperRecordTargetType enum provides the following commonly used values:

Target typeDescription
WORKWork items (issues, tickets, tasks, etc.)
PARTParts (capabilities, products, features, components, etc.)
USERDevRev users (includes all user types: dev users, sys users, and rev users)
ACCOUNTCustomer accounts
ARTIFACTAttachments and files
CONVERSATIONConversations
LINKLinks between objects
TAGTags
GROUPGroups
TIMELINE_COMMENTComments
ARTICLEKnowledge base articles
CUSTOM_OBJECTCustom object types
INCIDENTIncidents

Additional target types include ACCESS_CONTROL_ENTRY, AIRDROP_AUTHORIZATION_POLICY, AIRDROP_FIELD_AUTHORIZATION_POLICY, AIRDROP_PLATFORM_GROUP, CHAT, DIRECTORY, MEETING, OBJECT_MEMBER, REV_ORG, ROLE, and ROLE_SET.

Additional sync mapper record fields

secondary_ids

Optional map that labels values in external_ids with their usage context. Use when an external system requires different identifiers for different API calls (for example, a UUID for one endpoint and a login username for another).

await adapter.mappers.create({
  sync_unit: adapter.event.payload.event_context.sync_unit,
  external_ids: ["2a1c-uuid", "john_doe"],
  secondary_ids: { username: "john_doe" },
  targets: [devrevEntityId],
  status: SyncMapperRecordStatus.OPERATIONAL,
});

Values in secondary_ids are not indexed. If you need to look up by a secondary value, you must also include that value in external_ids.

external_versions

Records external-system changes to prevent update loops in bidirectional sync. When you create or update an object in the external system during loading, add that object's modified_date here. Later, when the object is extracted and the loader evaluates whether to apply it, if the modified_date is present in this list, the update is skipped (because the change originated in DevRev).

The recipe_version field is managed internally by the SDK (currently set to 0). Connectors do not need to determine or provide a meaningful recipe version themselves.

await adapter.mappers.update({
  id: syncMapperRecordId,
  sync_unit: adapter.event.payload.event_context.sync_unit,
  external_ids: { add: [externalId] },
  targets: { add: [devrevEntityId] },
  status: SyncMapperRecordStatus.OPERATIONAL,
  external_versions: {
    add: [{ recipe_version: 0, modified_date: "2024-01-15T10:30:00Z" }],
  },
});

extra_data

Free-form string field for storing any additional connector-specific data on the sync mapper record.

input_files

Optionally populated with the file name of the input file containing the object data. Useful for debugging - helps locate objects in the original extraction artifacts.

Last updated on