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

ITE-146 Added OnDelete #13

Merged
merged 1 commit into from
Nov 18, 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
@@ -0,0 +1,97 @@
import { createTestEvent, createTestIntegration } from '@segment/actions-core'
import nock from 'nock'
import Definition from '../index'
import { GQL_ENDPOINT } from '../functions'

const testDestination = createTestIntegration(Definition)

describe('StackAdapt Audiences - Destination Tests', () => {
const mockSettings = { apiKey: 'test-api-key' }
const gqlHostUrl = 'https://api.stackadapt.com'

afterEach(() => {
nock.cleanAll()
})

describe('testAuthentication', () => {
it('should validate authentication inputs', async () => {
nock(GQL_ENDPOINT, {
reqheaders: {
authorization: `Bearer ${mockSettings.apiKey}`,
'content-type': 'application/json'
}
})
.post('', {
query: /tokenInfo/
})
.reply(200, {
data: {
tokenInfo: {
scopesByAdvertiser: {
nodes: [
{
advertiser: { name: 'Test Advertiser' },
scopes: ['WRITE']
}
]
}
}
}
})

await expect(testDestination.testAuthentication(mockSettings)).resolves.not.toThrowError()
})

it('should fail if authentication is invalid', async () => {
nock(GQL_ENDPOINT).post('').reply(403, {})

await expect(testDestination.testAuthentication(mockSettings)).rejects.toThrowError('403Forbidden')
})
})

describe('onDelete', () => {
it('should delete a user with a given userId', async () => {
const userId = '9999'
const event = createTestEvent({ userId, type: 'identify' })

// Mock the GraphQL deleteProfilesWithExternalIds mutation
nock(gqlHostUrl)
.post('/graphql', (body) => {
return body.query.includes('deleteProfilesWithExternalIds') && body.query.includes(userId)
})
.reply(200, {
data: { deleteProfilesWithExternalIds: { userErrors: [] } }
})

const response = await testDestination.onDelete!(event, {
apiKey: 'test-api-key'
})

expect(response).toMatchObject({
data: { deleteProfilesWithExternalIds: { userErrors: [] } }
})
})

it('should throw an error if profile deletion fails with userErrors', async () => {
const userId = '9999'
const event = createTestEvent({ userId, type: 'identify' })

// Mock the GraphQL deleteProfilesWithExternalIds mutation with an error
nock(gqlHostUrl)
.post('/graphql')
.reply(200, {
data: {
deleteProfilesWithExternalIds: {
userErrors: [{ message: 'Deletion failed' }]
}
}
})

await expect(
testDestination.onDelete!(event, {
apiKey: 'test-api-key'
})
).rejects.toThrowError('Profile deletion was not successful: Deletion failed')
})
})
})
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { DestinationDefinition } from '@segment/actions-core'
import type { Settings } from './generated-types'

import { IntegrationError } from '@segment/actions-core'
import forwardProfile from './forwardProfile'
import forwardAudienceEvent from './forwardAudienceEvent'
import { AdvertiserScopesResponse } from './types'
import { GQL_ENDPOINT } from './functions'
import { GQL_ENDPOINT, EXTERNAL_PROVIDER, sha256hash } from './functions'

const destination: DestinationDefinition<Settings> = {
name: 'StackAdapt Audiences',
Expand Down Expand Up @@ -59,6 +59,52 @@ const destination: DestinationDefinition<Settings> = {
}
}
},
onDelete: async (request, { payload }) => {
const userId = payload.userId
const formattedExternalIds = `["${userId}"]`

const syncId = sha256hash(String(userId))

const mutation = `mutation {
deleteProfilesWithExternalIds(
externalIds: ${formattedExternalIds},
externalProvider: "${EXTERNAL_PROVIDER}",
syncId: "${syncId}"
) {
userErrors {
message
path
}
}
}`

try {
const response = await request(GQL_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: mutation })
})

const result = await response.json()

if (result.data.deleteProfilesWithExternalIds.userErrors.length > 0) {
const errorMessages = result.data.deleteProfilesWithExternalIds.userErrors.map((e: any) => e.message).join(', ')
throw new IntegrationError(`Profile deletion was not successful: ${errorMessages}`, 'DELETE_FAILED', 400)
}

return result
} catch (error) {
if (error instanceof IntegrationError) {
throw error
}
throw new IntegrationError(
`Unexpected error occurred in onDelete: ${error instanceof Error ? error.message : String(error)}`,
'UNKNOWN_ERROR',
400
)
}
},

actions: {
forwardProfile,
forwardAudienceEvent
Expand Down
Loading