Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Salesforce custom object external #2485

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ any of the tasks you completed below during your testing._
- [ ] Tested end-to-end using the [local server](https://github.com/segmentio/action-destinations/blob/main/docs/testing.md#local-end-to-end-testing)
- [ ] [If destination is already live] Tested for backward compatibility of destination. **Note:** New required fields are a breaking change.
- [ ] [Segmenters] Tested in the staging environment
- [ ] [Segmenters] [If applicable for this change] Tested for regression with Hadron.
- [ ] [Segmenters] [If applicable for this change] Tested for regression with Hadron.
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,7 @@ const destination: AudienceDestinationDefinition<Settings, AudienceSettings> = {
},

async createAudience(request, createAudienceInput) {
const {
settings,
audienceSettings: { audience_name } = {},
personas
} = createAudienceInput
const { settings, audienceSettings: { audience_name } = {}, personas } = createAudienceInput

if (!audience_name) {
throw new IntegrationError('Missing Audience Name', 'MISSING_REQUIRED_FIELD', 400)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,17 @@ export default action
// remove any key value pairs where the value is not a true or false string
function filterMailingLists(obj: { [key: string]: unknown }): { [key: string]: boolean } {
const result: { [key: string]: boolean } = {}
for (const key in obj) {
const value = obj[key];
for (const key in obj) {
const value = obj[key]

if (typeof value === 'string') {
if (value === 'true') {
result[key] = true;
result[key] = true
} else if (value === 'false') {
result[key] = false;
result[key] = false
}
} else if (typeof value === 'boolean') {
result[key] = value;
result[key] = value
}
}
return result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ export const PAGE = 'page'
export const TRACK = 'track'

export const SUPPORTED_TYPES = [PAGE, TRACK]

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ActionDefinition } from '@segment/actions-core'
import type { Settings } from '../generated-types'
import type { Payload } from './generated-types'
import { fields } from './fields'
import { send } from './utils'
import { send } from './utils'

const action: ActionDefinition<Settings, Payload> = {
title: 'Track Event',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const EMAIL_SCHEMA_NAME = 'EMAIL_SHA256'
export const MAID_SCHEMA_NAME = 'MAID_SHA256'
export const MAID_SCHEMA_NAME = 'MAID_SHA256'
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,40 @@ interface IDArray extends Array<string> {}

// POST https://ads-api.reddit.com/api/v3/custom_audiences/{audience_id}/users
export interface PopulateAudienceJSON {
data: {
action_type: 'ADD' | 'REMOVE'
column_order: string[] // EMAIL_SHA256 MAID_SHA256
user_data: Array<IDArray>
}
data: {
action_type: 'ADD' | 'REMOVE'
column_order: string[] // EMAIL_SHA256 MAID_SHA256
user_data: Array<IDArray>
}
}

// POST https://ads-api.reddit.com/api/v3/ad_accounts/{ad_account_id}/custom_audiences
export interface CreateAudienceJSON {
data: {
name: string
type: 'CUSTOMER_LIST'
}
data: {
name: string
type: 'CUSTOMER_LIST'
}
}

// 200 expected
export interface CreateAudienceResp {
data: {
name: string,
id: string,
status: "VALID"
}
data: {
name: string
id: string
status: 'VALID'
}
}

// 400 expected
export interface CreateAudienceRespError {
error: {
code: number,
message: string
}
error: {
code: number
message: string
}
}

// POST https://www.reddit.com/api/v1/access_token
export interface RedditAccessTokenRequest {
grant_type: string,
refresh_token: string
}
grant_type: string
refresh_token: string
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@

import { MAID_SCHEMA_NAME, EMAIL_SCHEMA_NAME } from './const'

export interface CreateAudienceResp {
id: string
id: string
}

export interface CreateAudienceReq {
data: {
name: string
type: string
}
data: {
name: string
type: string
}
}

export interface UpdateAudienceReq {
data: {
column_order: Columns
user_data: string[][],
action_type: 'ADD' | 'REMOVE'
}
data: {
column_order: Columns
user_data: string[][]
action_type: 'ADD' | 'REMOVE'
}
}

export type Columns = (typeof MAID_SCHEMA_NAME | typeof EMAIL_SCHEMA_NAME)[]
export type Columns = (typeof MAID_SCHEMA_NAME | typeof EMAIL_SCHEMA_NAME)[]
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,3 @@ const hash = (value: string | undefined): string | undefined => {
hash.update(value)
return hash.digest('hex')
}

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ export interface RawMapping {
}

export interface ColumnHeader {
cleanName: string
cleanName: string
originalName: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import nock from 'nock'
import { createTestEvent, createTestIntegration } from '@segment/actions-core'
import Destination from '../index'
import { API_VERSION } from '../sf-operations'

const testDestination = createTestIntegration(Destination)

const settings = {
instanceUrl: 'https://test.salesforce.com'
}

describe('Salesforce', () => {
describe('Custom Object by External Id', () => {
it('Should end events successfully', async () => {
const customObjectName = 'TestCustom__c'
const objectExternalId = 'Prospect__c'
const externalIdValue = '123456-7890-ABCD-0123-45678901234567'
const event = createTestEvent({
type: 'track',
event: 'Create Custom Object',
properties: {
email: '[email protected]',
company: 'Krusty Krab',
last_name: 'Squarepants',
object_type: objectExternalId
},
userId: externalIdValue
})

nock(`${settings.instanceUrl}/services/data/${API_VERSION}/sobjects`)
.patch(`/${customObjectName}/${objectExternalId}/${externalIdValue}`)
.reply(200, {})

const responses = await testDestination.testAction('customObjectExternalId', {
event,
settings,
mapping: {
operation: 'create',
customObjectName: customObjectName,
externalIdField: {
'@path': '$.properties.object_type'
},
externalIdValue: {
'@path': '$.userId'
},
customFields: {
'@path': '$.properties'
}
}
})

expect(responses.length).toBe(1)
expect(responses[0].status).toBe(200)
})
})
})

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { ActionDefinition, ExecuteInput, RequestClient } from '@segment/actions-core'
import type { Settings } from '../generated-types'
import type { Payload } from './generated-types'
import { customFields, operation } from '../sf-properties'
import Salesforce, { generateSalesforceRequest } from '../sf-operations'

const action: ActionDefinition<Settings, Payload> = {
title: 'Custom Object by External Id',
description:
'Create, update, or upsert records in any custom or standard object in Salesforce, using its External ID.',
fields: {
operation: operation,
customObjectName: {
label: 'Salesforce Object',
description:
'The API name of the Salesforce object that records will be added or updated within. This can be a standard or custom object. Custom objects must be predefined in your Salesforce account and should end with "__c".',
type: 'string',
required: true,
dynamic: true
},
externalIdField: {
label: 'External ID Field',
description: 'The name of the field that will be used as the External ID.',
type: 'string',
required: true
},
externalIdValue: {
label: 'External Id Value',
description: 'The external id field value that will be used to update the record.',
type: 'string',
required: true
},
customFields: customFields
},
dynamicFields: {
customObjectName: async (request: RequestClient, data: ExecuteInput<Settings, Payload, any, any, any>) => {
const salesforceInstance: Salesforce = new Salesforce(
data.settings.instanceUrl,
await generateSalesforceRequest(data.settings, request)
)

return salesforceInstance.customObjectName()
}
},
perform: async (request, { settings, payload }) => {
const sf: Salesforce = new Salesforce(settings.instanceUrl, await generateSalesforceRequest(settings, request))

return sf.upsertCustomObject(payload, payload.customObjectName)
}
}
rvadera12 marked this conversation as resolved.
Show resolved Hide resolved

export default action
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import contact from './contact'
import account from './account'
import { authenticateWithPassword } from './sf-operations'

import customObjectExternalId from './customObjectExternalId'
import lead2 from './lead2'
import contact2 from './contact2'
import cases2 from './cases2'
Expand Down Expand Up @@ -126,6 +127,7 @@ const destination: DestinationDefinition<Settings> = {
contact,
opportunity,
account,
customObjectExternalId,
lead2,
customObject2,
account2,
Expand Down
Loading
Loading