Skip to content

Commit

Permalink
feat(backend): formating + add basic tenant query resolvers test
Browse files Browse the repository at this point in the history
  • Loading branch information
golobitch committed Sep 3, 2024
1 parent 2859b3b commit d9dda53
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 40 deletions.
2 changes: 2 additions & 0 deletions packages/backend/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ process.env.USE_TIGERBEETLE = false
process.env.ENABLE_TELEMETRY = false
process.env.KRATOS_ADMIN_URL = 'http://127.0.0.1:4434/admin'
process.env.KRATOS_ADMIN_EMAIL = '[email protected]'
process.env.AUTH_ADMIN_URL = 'http://example.com/graphql'
process.env.AUTH_ADMIN_API_SECRET = 'verysecuresecret'

module.exports = {
...baseConfig,
Expand Down
32 changes: 14 additions & 18 deletions packages/backend/migrations/20240902220115_add_tenant_name.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,17 @@
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema
.table('tenants', function (table) {
table.string('name').unique().notNullable()
})
}

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema
.table('tenants', function (table) {
table.dropColumn('name')
})

}

return knex.schema.table('tenants', function (table) {
table.string('name').unique().notNullable()
})
}

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.table('tenants', function (table) {
table.dropColumn('name')
})
}
1 change: 0 additions & 1 deletion packages/backend/src/graphql/resolvers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ import { getCombinedPayments } from './combined_payments'
import { createOrUpdatePeerByUrl } from './auto-peering'
import { getAccountingTransfers } from './accounting_transfer'
import { createTenant, getTenant, getTenants } from './tenant'
import { getTenantEndpoints } from './tenant_endpoints'

export const resolvers: Resolvers = {
UInt8: GraphQLUInt8,
Expand Down
115 changes: 115 additions & 0 deletions packages/backend/src/graphql/resolvers/tenant.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { Knex } from 'knex'
import { IocContract } from '@adonisjs/fold'
import { createTestApp, TestContainer } from '../../tests/app'
import { AppServices } from '../../app'
import { initIocContainer } from '../..'
import { Config, IAppConfig } from '../../config/app'
import { truncateTables } from '../../tests/tableManager'
import { getPageTests } from './page.test'
import {
createTenant,
mockAdminAuthApiTenantCreation
} from '../../tests/tenant'
import { ApolloError, gql } from '@apollo/client'
import { Scope } from 'nock'
import { v4 as uuidv4 } from 'uuid'
import { errorToCode, errorToMessage, TenantError } from '../../tenant/errors'

describe('Tenant Resolver', (): void => {
let deps: IocContract<AppServices>
let appContainer: TestContainer
let knex: Knex
let config: IAppConfig
let scope: Scope

beforeAll(async (): Promise<void> => {
deps = initIocContainer(Config)
appContainer = await createTestApp(deps)
knex = appContainer.knex
config = await deps.use('config')
scope = mockAdminAuthApiTenantCreation(config.authAdminApiUrl).persist()
})

afterEach(async (): Promise<void> => {
await truncateTables(knex)
})

afterAll(async (): Promise<void> => {
scope.done()
appContainer.apolloClient.stop()
await appContainer.shutdown()
})

describe('Tenant Queries', (): void => {
getPageTests({
getClient: () => appContainer.apolloClient,
createModel: async () => createTenant(deps),
pagedQuery: 'tenants'
})

test('should return error if tenant does not exists', async (): Promise<void> => {
try {
await appContainer.apolloClient
.query({
query: gql`
query GetTenant($id: ID!) {
tenant(id: $id) {
id
name
kratosIdentityId
}
}
`,
variables: {
id: uuidv4()
}
})
.then((query) => {
if (query.data) return query.data.tenant
throw new Error('Data was empty')
})
} catch (error) {
expect(error).toBeInstanceOf(ApolloError)
expect((error as ApolloError).graphQLErrors).toContainEqual(
expect.objectContaining({
message: errorToMessage[TenantError.UnknownTenant],
extensions: expect.objectContaining({
code: errorToCode[TenantError.UnknownTenant]
})
})
)
}
})

test('should get correct tenant', async (): Promise<void> => {
const tenant = await createTenant(deps)

const response = await appContainer.apolloClient
.query({
query: gql`
query GetTenant($id: ID!) {
tenant(id: $id) {
id
name
kratosIdentityId
}
}
`,
variables: {
id: tenant.id
}
})
.then((query) => {
if (query.data) return query.data.tenant
throw new Error('Data was empty')
})

expect(response).toEqual({
__typename: 'Tenant',
id: tenant.id,
name: tenant.name,
kratosIdentityId: tenant.kratosIdentityId
})
})
})
})
2 changes: 1 addition & 1 deletion packages/backend/src/tenant/endpoints/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class TenantEndpoint extends WeakModel {
// Tell Objection.js that there is no single id column
// Define the composite primary key
static get idColumn() {
return ['tenantId', 'type'];
return ['tenantId', 'type']
}

public type!: EndpointType
Expand Down
14 changes: 8 additions & 6 deletions packages/backend/src/tenant/endpoints/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,11 @@ async function getTenantEndpointsPage(
pagination?: Pagination,
sortOrder?: SortOrder
) {
const data = await TenantEndpoint
.query(deps.knex)
.getPage(pagination, sortOrder)
return data
const data = await TenantEndpoint.query(deps.knex).getPage(
pagination,
sortOrder
)
return data
}

async function createTenantEndpoint(
Expand All @@ -74,8 +75,9 @@ async function createTenantEndpoint(
tenantId: createOptions.tenantId
}))

return await TenantEndpoint.query(createOptions.trx)
.insert(tenantEndpointsData)
return await TenantEndpoint.query(createOptions.trx).insert(
tenantEndpointsData
)
}

async function getEndpointsForTenant(
Expand Down
12 changes: 4 additions & 8 deletions packages/backend/src/tenant/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import {
import { v4 as uuidv4 } from 'uuid'
import { Pagination, SortOrder } from '../shared/baseModel'
import { EndpointOptions, TenantEndpointService } from './endpoints/service'
import { isTenantEndpointError } from './endpoints/errors'
import { tr } from '@faker-js/faker'

export interface CreateTenantOptions {
name: string
Expand Down Expand Up @@ -61,16 +59,15 @@ async function getTenantsPage(
sortOrder?: SortOrder
): Promise<Tenant[]> {
return await Tenant.query(deps.knex)
.withGraphFetched('endpoints')
.getPage(pagination, sortOrder)
}

async function getTenant(
deps: ServiceDependencies,
id: string
): Promise<Tenant | undefined> {
return Tenant.query(deps.knex)
.withGraphFetched('endpoints')
.findById(id)
return Tenant.query(deps.knex).withGraphFetched('endpoints').findById(id)
}

async function createTenant(
Expand All @@ -92,9 +89,8 @@ async function createTenant(
kratosIdentityId: uuidv4(),
endpoints: options.endpoints
}

tenant = await Tenant.query(trx)
.insertGraphAndFetch(tenantData)

tenant = await Tenant.query(trx).insertGraphAndFetch(tenantData)

// call auth admin api
const mutation = gql`
Expand Down
13 changes: 7 additions & 6 deletions packages/backend/src/tests/app.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import Axios from 'axios'
import { Knex } from 'knex'
import fetch from 'cross-fetch'
import { IocContract } from '@adonisjs/fold'
import {
ApolloClient,
ApolloLink,
createHttpLink,
InMemoryCache,
NormalizedCacheObject,
createHttpLink
NormalizedCacheObject
} from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
import { start, gracefulShutdown } from '..'
import { onError } from '@apollo/client/link/error'

import { App, AppServices } from '../app'
import { setContext } from '@apollo/client/link/context'
import { onError } from '@apollo/client/link/error'

export const testAccessToken = 'test-app-access'

Expand All @@ -23,6 +22,7 @@ export interface TestContainer {
app: App
knex: Knex
apolloClient: ApolloClient<NormalizedCacheObject>
authApolloClient: ApolloClient<NormalizedCacheObject>
connectionUrl: string
shutdown: () => Promise<void>
container: IocContract<AppServices>
Expand All @@ -38,7 +38,6 @@ export const createTestApp = async (
config.autoPeeringServerPort = 0
config.openPaymentsUrl = 'https://op.example'
config.walletAddressUrl = 'https://wallet.example/.well-known/pay'
const logger = await container.use('logger')

const app = new App(container)
await start(container, app)
Expand All @@ -57,6 +56,7 @@ export const createTestApp = async (
.persist()

const knex = await container.use('knex')
const logger = await container.use('logger')

const httpLink = createHttpLink({
uri: `http://localhost:${app.getAdminPort()}/graphql`,
Expand Down Expand Up @@ -108,6 +108,7 @@ export const createTestApp = async (
openPaymentsPort: app.getOpenPaymentsPort(),
knex,
apolloClient: client,
authApolloClient: await container.use('apolloClient'),
connectionUrl: config.databaseUrl,
shutdown: async () => {
nock.cleanAll()
Expand Down
46 changes: 46 additions & 0 deletions packages/backend/src/tests/tenant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { IocContract } from '@adonisjs/fold'
import { AppServices } from '../app'
import { Tenant } from '../tenant/model'
import { CreateTenantOptions } from '../tenant/service'
import { v4 as uuidv4 } from 'uuid'
import { EndpointType } from '../tenant/endpoints/model'
import nock from 'nock'

export function mockAdminAuthApiTenantCreation(mockedUrl: string) {
const url = new URL(mockedUrl)
return nock(`${url.protocol}//${url.host}`)
.post(/.*/)
.reply(200, {
data: {
createTenant: {
tenant: {
id: uuidv4()
}
}
}
})
}

export async function createTenant(
deps: IocContract<AppServices>
): Promise<Tenant> {
const tenantService = await deps.use('tenantService')

const options: CreateTenantOptions = {
name: uuidv4(),
idpConsentEndpoint: `https://example.com/${uuidv4()}`,
idpSecret: `secret-${uuidv4()}`,
endpoints: [
{
type: EndpointType.RatesUrl,
value: `https://example.com/rates/${uuidv4()}`
},
{
type: EndpointType.WebhookBaseUrl,
value: `https://example.com/webhook/${uuidv4()}`
}
]
}

return tenantService.create(options) as Promise<Tenant>
}

0 comments on commit d9dda53

Please sign in to comment.