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

fixing auth for taboola #2107

Merged
merged 2 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,17 @@ const accountId = 'TestAccount'
const audienceId = '1111'
const createAudienceUrl = `https://backstage.taboola.com/backstage/api/1.0/${accountId}`

const createAudienceInput = {
settings: {},
audienceName: '',
audienceSettings: {
ttl_in_hours: 1024,
exclude_from_campaigns: false
}
}
const getAudienceInput = {
externalId: audienceId,
settings: {}
settings: {
client_id: 'test_client_id',
client_secret: 'test_client'
},
audienceSettings: {}
}



describe('Taboola (actions)', () => {
describe('testAuthentication', () => {
it('should validate authentication inputs', async () => {
Expand All @@ -35,25 +33,62 @@ describe('Taboola (actions)', () => {

describe('createAudience', () => {
it('should fail if no audience name is set', async () => {
await expect(testDestination.createAudience(createAudienceInput)).rejects.toThrowError(

const createAudienceInput1 = {
settings: {
client_id: 'test_client_id',
client_secret: 'test_client'
},
audienceName: '',
audienceSettings: {
ttl_in_hours: 1024,
exclude_from_campaigns: false,
account_id:accountId
}
}

await expect(testDestination.createAudience(createAudienceInput1)).rejects.toThrowError(
new IntegrationError("Missing 'Audience Name' value", 'MISSING_REQUIRED_FIELD', 400)
)
})

it('should fail if no account ID is set', async () => {
createAudienceInput.audienceName = 'Test Audience'
await expect(testDestination.createAudience(createAudienceInput)).rejects.toThrowError(

const createAudienceInput2 = {
settings: {
client_id: 'test_client_id',
client_secret: 'test_client'
},
audienceName: 'Test Audience',
audienceSettings: {
ttl_in_hours: 1024,
exclude_from_campaigns: false,
account_id:''
}
}

await expect(testDestination.createAudience(createAudienceInput2)).rejects.toThrowError(
new IntegrationError("Missing 'Account ID' value", 'MISSING_REQUIRED_FIELD', 400)
)
})

it('should create a new Taboola Audience', async () => {
nock('https://backstage.taboola.com').post('/backstage/oauth/token').reply(200, { accessToken: 'some_token' })
nock(createAudienceUrl).post('/audience_onboarding/create').reply(200, { audience_id: '1234' })

createAudienceInput.audienceName = 'Test Audience'
createAudienceInput.audienceSettings.account_id = accountId

const r = await testDestination.createAudience(createAudienceInput)
const createAudienceInput3 = {
settings: {
client_id: 'test_client_id',
client_secret: 'test_client'
},
audienceName: 'Test Audience',
audienceSettings: {
ttl_in_hours: 1024,
exclude_from_campaigns: false,
account_id:accountId
}
}
const r = await testDestination.createAudience(createAudienceInput3)
expect(r).toEqual({ externalId: '1234' })
})
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import type { AudienceDestinationDefinition } from '@segment/actions-core'
import type { Settings, AudienceSettings } from './generated-types'
import { IntegrationError } from '@segment/actions-core'
import { TaboolaClient } from './syncAudience/client'

import syncAudience from './syncAudience'

interface RefreshTokenResponse {
access_token: string
}

const destination: AudienceDestinationDefinition<Settings, AudienceSettings> = {
name: 'Taboola (actions)',
slug: 'actions-taboola-actions',
mode: 'cloud',

authentication: {
scheme: 'oauth-managed',
scheme: 'oauth2',
fields: {
client_id: {
label: 'Client ID',
Expand All @@ -30,16 +27,7 @@ const destination: AudienceDestinationDefinition<Settings, AudienceSettings> = {
}
},
refreshAccessToken: async (request, { settings }) => {
const res = await request<RefreshTokenResponse>('https://backstage.taboola.com/backstage/oauth/token', {
method: 'POST',
body: new URLSearchParams({
client_id: settings.client_id,
client_secret: settings.client_secret,
grant_type: 'client_credentials'
})
})

return { accessToken: res.data.access_token }
return await TaboolaClient.refreshAccessToken(request, { settings })
}
},
extendRequest({ auth }) {
Expand Down Expand Up @@ -92,6 +80,10 @@ const destination: AudienceDestinationDefinition<Settings, AudienceSettings> = {
}

try {
const accessToken = (
await TaboolaClient.refreshAccessToken(request, { settings: createAudienceInput.settings })
).accessToken

const response = await request(
`https://backstage.taboola.com/backstage/api/1.0/${accountId}/audience_onboarding/create`,
{
Expand All @@ -100,9 +92,14 @@ const destination: AudienceDestinationDefinition<Settings, AudienceSettings> = {
audience_name: audienceName,
ttl_in_hours: ttlInHours,
exclude_from_campaigns: excludeFromCampaigns
},
headers: {
authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
}
)

const json = await response.json()

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createHash } from 'crypto'
import type { ModifiedResponse } from '@segment/actions-core'
import { RequestClient, IntegrationError } from '@segment/actions-core'
import { Payload } from './generated-types'
import { AudienceSettings } from '../generated-types'
import { AudienceSettings, Settings } from '../generated-types'

interface ClusterItem {
user_id: string
Expand All @@ -18,19 +18,45 @@ interface TaboolaPayload {
identities: Cluster[]
}

interface RefreshTokenResponse {
access_token: string
}

export class TaboolaClient {
request: RequestClient
payloads: Payload[]
audienceSettings: AudienceSettings
audienceSettings?: AudienceSettings

constructor(request: RequestClient, payloads: Payload[], audienceSettings: AudienceSettings) {
constructor(request: RequestClient, payloads: Payload[], audienceSettings?: AudienceSettings) {
this.request = request
this.payloads = payloads
this.audienceSettings = audienceSettings

if (!this.audienceSettings) {
throw new IntegrationError('Bad Request: no audienceSettings found.', 'INVALID_REQUEST_DATA', 400)
}

if (!this.audienceSettings.account_id) {
throw new IntegrationError('Bad Request: no audienceSettings.account_id found.', 'INVALID_REQUEST_DATA', 400)
}
}

async sendToTaboola() {
static async refreshAccessToken(request: RequestClient, { settings }: { settings: Settings }) {
const res = await request<RefreshTokenResponse>('https://backstage.taboola.com/backstage/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: settings.client_id,
client_secret: settings.client_secret,
grant_type: 'client_credentials'
})
})
return { accessToken: res.data.access_token }
}

async sendToTaboola() {
this.payloads.forEach((payload) => {
payload.action = payload.traits_or_props[payload.segment_computation_key] ? 'ADD' : 'REMOVE'
})
Expand Down Expand Up @@ -59,7 +85,9 @@ export class TaboolaClient {
if (identities.length > 0) {
taboolaRequests.push(
this.request(
`https://backstage.taboola.com/backstage/api/1.0/${this.audienceSettings.account_id}/audience_onboarding`,
`https://backstage.taboola.com/backstage/api/1.0/${
this.audienceSettings?.account_id as string
}/audience_onboarding`,
{
method: 'POST',
json: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ const action: ActionDefinition<Settings, Payload, AudienceSettings> = {
}
},
perform: (request, { payload, audienceSettings }) => {

if (!payload.external_audience_id) {
throw new IntegrationError('Bad Request: payload.external_audience_id missing.', 'INVALID_REQUEST_DATA', 400)
}
Expand All @@ -120,27 +119,10 @@ const action: ActionDefinition<Settings, Payload, AudienceSettings> = {
)
}

if (!audienceSettings) {
throw new IntegrationError('Bad Request: no audienceSettings found.', 'INVALID_REQUEST_DATA', 400)
}

if (!audienceSettings.account_id) {
throw new IntegrationError('Bad Request: no audienceSettings.account_id found.', 'INVALID_REQUEST_DATA', 400)
}

const taboolaClient = new TaboolaClient(request, [payload], audienceSettings)
return taboolaClient.sendToTaboola()
},
performBatch: async (request, { payload: payloads, audienceSettings }) => {

if (!audienceSettings) {
throw new IntegrationError('Bad Request: no audienceSettings found.', 'INVALID_REQUEST_DATA', 400)
}

if (!audienceSettings.account_id) {
throw new IntegrationError('Bad Request: no audienceSettings.account_id found.', 'INVALID_REQUEST_DATA', 400)
}

performBatch: (request, { payload: payloads, audienceSettings }) => {
const taboolaClient = new TaboolaClient(request, payloads, audienceSettings)
return taboolaClient.sendToTaboola()
}
Expand Down
Loading