Skip to content
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 @@ -59,7 +59,7 @@ describe('Open Payments Wallet Address Service', (): void => {
await appContainer.shutdown()
})

describe('Create or Get Wallet Address3', (): void => {
describe('Create or Get Wallet Address', (): void => {
let tenantId: string
let options: CreateOptions

Expand Down Expand Up @@ -606,6 +606,56 @@ describe('Open Payments Wallet Address Service', (): void => {
)
)

test(
'creates wallet address not found event for tenant with matching prefix',
withConfigOverride(
() => config,
{ walletAddressLookupTimeoutMs: 0 },
async (): Promise<void> => {
const walletAddressUrl = `https://${faker.internet.domainName()}/.well-known/pay`
const tenant = await createTenant(deps)
await createTenantSettings(deps, {
tenantId: tenant.id,
setting: [
{
key: TenantSettingKeys.WALLET_ADDRESS_URL.name,
value: `${walletAddressUrl}/${uuid()}`
}
]
})

await expect(
walletAddressService.getOrPollByUrl(walletAddressUrl)
).resolves.toBeUndefined()

const walletAddressNotFoundEvents = await WalletAddressEvent.query(
knex
)
.where({
type: WalletAddressEventType.WalletAddressNotFound
})
.withGraphFetched('webhooks')

expect(walletAddressNotFoundEvents).toHaveLength(1)
expect(walletAddressNotFoundEvents[0].webhooks).toHaveLength(2)
expect(walletAddressNotFoundEvents[0]).toMatchObject({
data: { walletAddressUrl },
webhooks: expect.arrayContaining([
expect.objectContaining({
recipientTenantId: tenant.id,
eventId: walletAddressNotFoundEvents[0].id,
processAt: expect.any(Date)
}),
expect.objectContaining({
recipientTenantId: config.operatorTenantId,
eventId: walletAddressNotFoundEvents[0].id,
processAt: expect.any(Date)
})
])
})
}
)
)
test(
'polls for wallet address',
withConfigOverride(
Expand Down
14 changes: 13 additions & 1 deletion packages/backend/src/open_payments/wallet_address/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,13 +379,25 @@ async function getOrPollByUrl(
const existingWalletAddress = await getWalletAddressByUrl(deps, url)
if (existingWalletAddress) return existingWalletAddress

let containsOperatorTenant = false
const webhookRecipients = (
await deps.tenantSettingService.getSettingsByPrefix(url)
).map((tenantSetting) => {
if (tenantSetting.tenantId === deps.config.operatorTenantId)
containsOperatorTenant = true
return { recipientTenantId: tenantSetting.tenantId }
})

if (!containsOperatorTenant)
webhookRecipients.push({ recipientTenantId: deps.config.operatorTenantId })

await WalletAddressEvent.query(deps.knex).insertGraph({
type: WalletAddressEventType.WalletAddressNotFound,
data: {
walletAddressUrl: url
},
tenantId: deps.config.operatorTenantId,
webhooks: [{ recipientTenantId: deps.config.operatorTenantId }]
webhooks: webhookRecipients
})

deps.logger.debug(
Expand Down
67 changes: 66 additions & 1 deletion packages/backend/src/tenants/settings/service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ import { Tenant } from '../model'
import { TenantService } from '../service'
import { faker } from '@faker-js/faker'
import { exchangeRatesSetting, randomSetting } from '../../tests/tenantSettings'
import { TenantSetting } from './model'
import { TenantSetting, TenantSettingKeys } from './model'
import {
CreateOptions,
GetOptions,
TenantSettingService,
UpdateOptions
} from './service'
import { AuthServiceClient } from '../../auth-service-client/client'
import { v4 as uuid } from 'uuid'
import { createTenant } from '../../tests/tenant'

describe('TenantSetting Service', (): void => {
let deps: IocContract<AppServices>
Expand Down Expand Up @@ -371,4 +373,67 @@ describe('TenantSetting Service', (): void => {
expect(result).toEqual([])
})
})

describe('get settings by value', (): void => {
test('can get settings by wallet address prefix setting', async (): Promise<void> => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add a negative test to check that we avoid fetching a tenant if the prefix doesn't match

const secondTenant = await createTenant(deps)
const baseUrl = `https://${faker.internet.domainName()}/${uuid()}`
const settings = (
await Promise.all([
tenantSettingService.create({
tenantId: tenant.id,
setting: [
{
key: TenantSettingKeys.WALLET_ADDRESS_URL.name,
value: `${baseUrl}/${uuid()}`
}
]
}),
tenantSettingService.create({
tenantId: secondTenant.id,
setting: [
{
key: TenantSettingKeys.WALLET_ADDRESS_URL.name,
value: `${baseUrl}/${uuid()}`
}
]
})
])
).flat()

const retrievedSettings =
await tenantSettingService.getSettingsByPrefix(baseUrl)
expect(retrievedSettings).toEqual(settings)
})

test('does not retrieve tenants if no wallet address prefix matches', async (): Promise<void> => {
const secondTenant = await createTenant(deps)
const baseUrl = `https://${faker.internet.domainName()}/${uuid()}`
await Promise.all([
tenantSettingService.create({
tenantId: tenant.id,
setting: [
{
key: TenantSettingKeys.WALLET_ADDRESS_URL.name,
value: `${baseUrl}/${uuid()}`
}
]
}),
tenantSettingService.create({
tenantId: secondTenant.id,
setting: [
{
key: TenantSettingKeys.WALLET_ADDRESS_URL.name,
value: `${baseUrl}/${uuid()}`
}
]
})
])

const retrievedSettings = await tenantSettingService.getSettingsByPrefix(
faker.internet.url()
)
expect(retrievedSettings).toHaveLength(0)
})
})
})
16 changes: 15 additions & 1 deletion packages/backend/src/tenants/settings/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface TenantSettingService {
pagination?: Pagination,
sortOrder?: SortOrder
) => Promise<TenantSetting[]>
getSettingsByPrefix: (prefix: string) => Promise<TenantSetting[]>
}

export interface ServiceDependencies extends BaseService {
Expand All @@ -68,7 +69,9 @@ export async function createTenantSettingService(
tenantId: string,
pagination?: Pagination,
sortOrder?: SortOrder
) => getTenantSettingPageForTenant(deps, tenantId, pagination, sortOrder)
) => getTenantSettingPageForTenant(deps, tenantId, pagination, sortOrder),
getSettingsByPrefix: (prefix: string) =>
getWalletAddressSettingsByPrefix(deps, prefix)
}
}

Expand Down Expand Up @@ -147,3 +150,14 @@ async function getTenantSettingPageForTenant(
.andWhere('tenantId', tenantId)
.getPage(pagination, sortOrder)
}

async function getWalletAddressSettingsByPrefix(
deps: ServiceDependencies,
prefix: string
): Promise<TenantSetting[]> {
return await TenantSetting.query(deps.knex)
.whereILike('value', `${prefix}%`)
.andWhere({
key: TenantSettingKeys.WALLET_ADDRESS_URL.name
})
}
Loading