mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-11 18:01:59 +00:00
UBERF-9726: Fix integrations in accounts for CR 24.1 (#8490)
Signed-off-by: Alexey Zinoviev <alexey.zinoviev@xored.com>
This commit is contained in:
parent
8ea386abe8
commit
66325de693
@ -146,21 +146,12 @@ export interface AccountClient {
|
||||
updateIntegration: (integration: Integration) => Promise<void>
|
||||
deleteIntegration: (integrationKey: IntegrationKey) => Promise<void>
|
||||
getIntegration: (integrationKey: IntegrationKey) => Promise<Integration | null>
|
||||
listIntegrations: (filter: {
|
||||
socialId?: PersonId
|
||||
kind?: string
|
||||
workspaceUuid?: WorkspaceUuid | null
|
||||
}) => Promise<Integration[]>
|
||||
listIntegrations: (filter: Partial<IntegrationKey>) => Promise<Integration[]>
|
||||
addIntegrationSecret: (integrationSecret: IntegrationSecret) => Promise<void>
|
||||
updateIntegrationSecret: (integrationSecret: IntegrationSecret) => Promise<void>
|
||||
deleteIntegrationSecret: (integrationSecretKey: IntegrationSecretKey) => Promise<void>
|
||||
getIntegrationSecret: (integrationSecretKey: IntegrationSecretKey) => Promise<IntegrationSecret | null>
|
||||
listIntegrationsSecrets: (filter: {
|
||||
socialId?: PersonId
|
||||
kind?: string
|
||||
workspaceUuid?: WorkspaceUuid | null
|
||||
key?: string
|
||||
}) => Promise<IntegrationSecret[]>
|
||||
listIntegrationsSecrets: (filter: Partial<IntegrationSecretKey>) => Promise<IntegrationSecret[]>
|
||||
getAccountInfo: (uuid: PersonUuid) => Promise<AccountInfo>
|
||||
|
||||
setCookie: () => Promise<void>
|
||||
@ -786,11 +777,7 @@ class AccountClientImpl implements AccountClient {
|
||||
return await this.rpc(request)
|
||||
}
|
||||
|
||||
async listIntegrations (filter: {
|
||||
socialId?: PersonId
|
||||
kind?: string
|
||||
workspaceUuid?: WorkspaceUuid | null
|
||||
}): Promise<Integration[]> {
|
||||
async listIntegrations (filter: Partial<IntegrationKey>): Promise<Integration[]> {
|
||||
const request = {
|
||||
method: 'listIntegrations' as const,
|
||||
params: filter
|
||||
@ -835,12 +822,7 @@ class AccountClientImpl implements AccountClient {
|
||||
return await this.rpc(request)
|
||||
}
|
||||
|
||||
async listIntegrationsSecrets (filter: {
|
||||
socialId?: PersonId
|
||||
kind?: string
|
||||
workspaceUuid?: WorkspaceUuid | null
|
||||
key?: string
|
||||
}): Promise<IntegrationSecret[]> {
|
||||
async listIntegrationsSecrets (filter: Partial<IntegrationSecretKey>): Promise<IntegrationSecret[]> {
|
||||
const request = {
|
||||
method: 'listIntegrationsSecrets' as const,
|
||||
params: filter
|
||||
|
@ -59,7 +59,7 @@ export interface MailboxInfo {
|
||||
export interface Integration {
|
||||
socialId: PersonId
|
||||
kind: string // Integration kind. E.g. 'github', 'mail', 'telegram-bot', 'telegram' etc.
|
||||
workspaceUuid?: WorkspaceUuid
|
||||
workspaceUuid: WorkspaceUuid | null
|
||||
data?: Record<string, any>
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ export type IntegrationKey = Omit<Integration, 'data'>
|
||||
export interface IntegrationSecret {
|
||||
socialId: PersonId
|
||||
kind: string // Integration kind. E.g. 'github', 'mail', 'telegram-bot', 'telegram' etc.
|
||||
workspaceUuid?: WorkspaceUuid
|
||||
workspaceUuid: WorkspaceUuid | null
|
||||
key: string // Key for the secret in the integration. Different secrets for the same integration must have different keys. Can be any string. E.g. '', 'user_app_1' etc.
|
||||
secret: string
|
||||
}
|
||||
|
@ -889,15 +889,14 @@ describe('integration methods', () => {
|
||||
expect(mockDb.integrationSecret.findOne).toHaveBeenCalledWith(mockSecretKey)
|
||||
})
|
||||
|
||||
test('should throw error when secret not found', async () => {
|
||||
test('should return null when integration secret not found', async () => {
|
||||
;(decodeTokenVerbose as jest.Mock).mockReturnValue({
|
||||
extra: { service: 'github' }
|
||||
})
|
||||
;(mockDb.integrationSecret.findOne as jest.Mock).mockResolvedValue(null)
|
||||
|
||||
await expect(getIntegrationSecret(mockCtx, mockDb, mockBranding, mockToken, mockSecretKey)).rejects.toThrow(
|
||||
new PlatformError(new Status(Severity.ERROR, platform.status.IntegrationSecretNotFound, {}))
|
||||
)
|
||||
const result = await getIntegrationSecret(mockCtx, mockDb, mockBranding, mockToken, mockSecretKey)
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
test('should throw error for unauthorized service', async () => {
|
||||
|
@ -62,6 +62,15 @@ interface MongoIndex {
|
||||
options: CreateIndexesOptions & { name: string }
|
||||
}
|
||||
|
||||
function getFilteredQuery<T> (query: Query<T>): Query<T> {
|
||||
return Object.entries(query).reduce<Query<T>>((acc, [key, value]) => {
|
||||
if (value !== undefined) {
|
||||
acc[key as keyof Query<T>] = value
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
export class MongoDbCollection<T extends Record<string, any>, K extends keyof T | undefined = undefined>
|
||||
implements DbCollection<T> {
|
||||
constructor (
|
||||
@ -123,11 +132,11 @@ implements DbCollection<T> {
|
||||
}
|
||||
|
||||
async find (query: Query<T>, sort?: Sort<T>, limit?: number): Promise<T[]> {
|
||||
return await this.findCursor(query, sort, limit).toArray()
|
||||
return await this.findCursor(getFilteredQuery(query), sort, limit).toArray()
|
||||
}
|
||||
|
||||
findCursor (query: Query<T>, sort?: Sort<T>, limit?: number): FindCursor<T> {
|
||||
const cursor = this.collection.find<T>(query as Filter<T>)
|
||||
const cursor = this.collection.find<T>(getFilteredQuery(query) as Filter<T>)
|
||||
|
||||
if (sort !== undefined) {
|
||||
cursor.sort(sort as RawSort)
|
||||
@ -147,7 +156,7 @@ implements DbCollection<T> {
|
||||
}
|
||||
|
||||
async findOne (query: Query<T>): Promise<T | null> {
|
||||
const doc = await this.collection.findOne<T>(query as Filter<T>)
|
||||
const doc = await this.collection.findOne<T>(getFilteredQuery(query) as Filter<T>)
|
||||
if (doc === null) {
|
||||
return null
|
||||
}
|
||||
@ -190,11 +199,11 @@ implements DbCollection<T> {
|
||||
}
|
||||
}
|
||||
|
||||
await this.collection.updateOne(query as Filter<T>, resOps)
|
||||
await this.collection.updateOne(getFilteredQuery(query) as Filter<T>, resOps)
|
||||
}
|
||||
|
||||
async deleteMany (query: Query<T>): Promise<void> {
|
||||
await this.collection.deleteMany(query as Filter<T>)
|
||||
await this.collection.deleteMany(getFilteredQuery(query) as Filter<T>)
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,7 +227,7 @@ export class AccountMongoDbCollection extends MongoDbCollection<Account, 'uuid'>
|
||||
}
|
||||
|
||||
async findOne (query: Query<Account>): Promise<Account | null> {
|
||||
const res = await this.collection.findOne<Account>(query as Filter<Account>)
|
||||
const res = await this.collection.findOne<Account>(getFilteredQuery(query) as Filter<Account>)
|
||||
|
||||
return res !== null ? this.convertToObj(res) : null
|
||||
}
|
||||
@ -247,7 +256,7 @@ export class WorkspaceStatusMongoDbCollection implements DbCollection<WorkspaceS
|
||||
private toWsQuery (query: Query<WorkspaceStatus>): Query<WorkspaceInfoWithStatus> {
|
||||
const res: Query<WorkspaceInfoWithStatus> = {}
|
||||
|
||||
for (const key of Object.keys(query)) {
|
||||
for (const key of Object.keys(getFilteredQuery(query))) {
|
||||
const qVal = (query as any)[key]
|
||||
if (key === 'workspaceUuid') {
|
||||
res.uuid = qVal
|
||||
|
@ -180,8 +180,8 @@ implements DbCollection<T> {
|
||||
break
|
||||
}
|
||||
default: {
|
||||
currIdx++
|
||||
if (qKey !== null) {
|
||||
currIdx++
|
||||
whereChunks.push(`"${snakeKey}" = ${formatVar(currIdx, castType)}`)
|
||||
values.push(qKey)
|
||||
} else {
|
||||
@ -955,10 +955,7 @@ export class PostgresAccountDB implements AccountDB {
|
||||
_def_ws_uuid UUID NOT NULL GENERATED ALWAYS AS (COALESCE(workspace_uuid, '00000000-0000-0000-0000-000000000000')) STORED NOT VISIBLE,
|
||||
key STRING,
|
||||
secret STRING NOT NULL,
|
||||
CONSTRAINT integration_secrets_pk PRIMARY KEY (social_id, kind, _def_ws_uuid, key),
|
||||
CONSTRAINT integration_secrets_integrations_fk FOREIGN KEY (social_id, kind, _def_ws_uuid)
|
||||
REFERENCES ${this.ns}.integrations(social_id, kind, _def_ws_uuid)
|
||||
ON DELETE CASCADE
|
||||
CONSTRAINT integration_secrets_pk PRIMARY KEY (social_id, kind, _def_ws_uuid, key)
|
||||
);
|
||||
`
|
||||
]
|
||||
|
@ -518,7 +518,7 @@ export async function createIntegration (
|
||||
verifyAllowedServices(integrationServices, extra)
|
||||
const { socialId, kind, workspaceUuid, data } = params
|
||||
|
||||
if (kind == null || socialId == null) {
|
||||
if (kind == null || socialId == null || workspaceUuid === undefined) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
|
||||
}
|
||||
|
||||
@ -553,6 +553,10 @@ export async function updateIntegration (
|
||||
verifyAllowedServices(integrationServices, extra)
|
||||
const { socialId, kind, workspaceUuid, data } = params
|
||||
|
||||
if (kind == null || socialId == null || workspaceUuid === undefined) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
|
||||
}
|
||||
|
||||
const existing = await db.integration.findOne({ socialId, kind, workspaceUuid })
|
||||
if (existing == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.IntegrationNotFound, {}))
|
||||
@ -572,11 +576,16 @@ export async function deleteIntegration (
|
||||
verifyAllowedServices(integrationServices, extra)
|
||||
const { socialId, kind, workspaceUuid } = params
|
||||
|
||||
if (kind == null || socialId == null || workspaceUuid === undefined) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
|
||||
}
|
||||
|
||||
const existing = await db.integration.findOne({ socialId, kind, workspaceUuid })
|
||||
if (existing == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.IntegrationNotFound, {}))
|
||||
}
|
||||
|
||||
await db.integrationSecret.deleteMany({ socialId, kind, workspaceUuid })
|
||||
await db.integration.deleteMany({ socialId, kind, workspaceUuid })
|
||||
}
|
||||
|
||||
@ -585,11 +594,7 @@ export async function listIntegrations (
|
||||
db: AccountDB,
|
||||
branding: Branding | null,
|
||||
token: string,
|
||||
params: {
|
||||
socialId?: PersonId
|
||||
kind?: string
|
||||
workspaceUuid?: WorkspaceUuid | null
|
||||
}
|
||||
params: Partial<IntegrationKey>
|
||||
): Promise<Integration[]> {
|
||||
const { account, extra } = decodeTokenVerbose(ctx, token)
|
||||
const isAllowedService = verifyAllowedServices(integrationServices, extra, false)
|
||||
@ -636,6 +641,10 @@ export async function getIntegration (
|
||||
const isAllowedService = verifyAllowedServices(integrationServices, extra, false)
|
||||
const { socialId, kind, workspaceUuid } = params
|
||||
|
||||
if (kind == null || socialId == null || workspaceUuid === undefined) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
|
||||
}
|
||||
|
||||
if (!isAllowedService) {
|
||||
const existingSocialId = await db.socialId.findOne({ _id: socialId, personUuid: account, verifiedOn: { $gt: 0 } })
|
||||
|
||||
@ -658,7 +667,7 @@ export async function addIntegrationSecret (
|
||||
verifyAllowedServices(integrationServices, extra)
|
||||
const { socialId, kind, workspaceUuid, key, secret } = params
|
||||
|
||||
if (kind == null || socialId == null || key == null) {
|
||||
if (kind == null || socialId == null || workspaceUuid === undefined || key == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
|
||||
}
|
||||
|
||||
@ -690,6 +699,10 @@ export async function updateIntegrationSecret (
|
||||
const { socialId, kind, workspaceUuid, key, secret } = params
|
||||
const secretKey: IntegrationSecretKey = { socialId, kind, workspaceUuid, key }
|
||||
|
||||
if (kind == null || socialId == null || workspaceUuid === undefined || key == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
|
||||
}
|
||||
|
||||
const existingSecret = await db.integrationSecret.findOne(secretKey)
|
||||
if (existingSecret == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.IntegrationSecretNotFound, {}))
|
||||
@ -710,6 +723,10 @@ export async function deleteIntegrationSecret (
|
||||
const { socialId, kind, workspaceUuid, key } = params
|
||||
const secretKey: IntegrationSecretKey = { socialId, kind, workspaceUuid, key }
|
||||
|
||||
if (kind == null || socialId == null || workspaceUuid === undefined || key == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
|
||||
}
|
||||
|
||||
const existingSecret = await db.integrationSecret.findOne(secretKey)
|
||||
if (existingSecret == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.IntegrationSecretNotFound, {}))
|
||||
@ -729,11 +746,12 @@ export async function getIntegrationSecret (
|
||||
verifyAllowedServices(integrationServices, extra)
|
||||
const { socialId, kind, workspaceUuid, key } = params
|
||||
|
||||
const existing = await db.integrationSecret.findOne({ socialId, kind, workspaceUuid, key })
|
||||
if (existing == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.IntegrationSecretNotFound, {}))
|
||||
if (kind == null || socialId == null || workspaceUuid === undefined || key == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
|
||||
}
|
||||
|
||||
const existing = await db.integrationSecret.findOne({ socialId, kind, workspaceUuid, key })
|
||||
|
||||
return existing
|
||||
}
|
||||
|
||||
@ -742,12 +760,7 @@ export async function listIntegrationsSecrets (
|
||||
db: AccountDB,
|
||||
branding: Branding | null,
|
||||
token: string,
|
||||
params: {
|
||||
socialId?: PersonId
|
||||
kind?: string
|
||||
workspaceUuid?: WorkspaceUuid | null
|
||||
key?: string
|
||||
}
|
||||
params: Partial<IntegrationSecretKey>
|
||||
): Promise<IntegrationSecret[]> {
|
||||
const { extra } = decodeTokenVerbose(ctx, token)
|
||||
verifyAllowedServices(integrationServices, extra)
|
||||
|
@ -142,7 +142,7 @@ export interface MailboxInfo {
|
||||
export interface Integration {
|
||||
socialId: PersonId
|
||||
kind: string // Integration kind. E.g. 'github', 'mail', 'telegram-bot', 'telegram' etc.
|
||||
workspaceUuid?: WorkspaceUuid
|
||||
workspaceUuid: WorkspaceUuid | null
|
||||
data?: Record<string, any>
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ export type IntegrationKey = Omit<Integration, 'data'>
|
||||
export interface IntegrationSecret {
|
||||
socialId: PersonId
|
||||
kind: string // Integration kind. E.g. 'github', 'mail', 'telegram-bot', 'telegram' etc.
|
||||
workspaceUuid?: WorkspaceUuid
|
||||
workspaceUuid: WorkspaceUuid | null
|
||||
key: string // Key for the secret in the integration. Different secrets for the same integration must have different keys. Can be any string. E.g. '', 'user_app_1' etc.
|
||||
secret: string
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ services:
|
||||
- 27018:27018
|
||||
restart: unless-stopped
|
||||
cockroach:
|
||||
image: cockroachdb/cockroach:latest-v24.2
|
||||
image: cockroachdb/cockroach:v24.1.2
|
||||
ports:
|
||||
- '26258:26257'
|
||||
- '18089:8080'
|
||||
|
@ -3,6 +3,7 @@ PLATFORM_TRANSACTOR='ws://localhost:3334'
|
||||
STAGING_URL='https://front.hc.engineering'
|
||||
PLATFORM_USER='user1'
|
||||
PLATFORM_USER_SECOND='user2'
|
||||
PLATFORM_ADMIN='admin'
|
||||
PLATFORM_WS='sanity-ws'
|
||||
PLATFORM_TOKEN='eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6InVzZXIxIiwid29ya3NwYWNlIjoic2FuaXR5LXdzIn0.hfUCqePHO-WNps2by4B-CYGKIpDpLG0WVCUUtU-SVI4'
|
||||
LOCAL_URL='http://localhost:3003/'
|
||||
|
@ -51,6 +51,7 @@
|
||||
"cross-env": "~7.0.3",
|
||||
"@hcengineering/core": "^0.6.32",
|
||||
"@hcengineering/client-resources": "^0.6.27",
|
||||
"@hcengineering/account": "^0.6.0"
|
||||
"@hcengineering/account": "^0.6.0",
|
||||
"@hcengineering/account-client": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
20
tests/sanity/tests/API/AccountClient.ts
Normal file
20
tests/sanity/tests/API/AccountClient.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { getClient as getClientRaw, type AccountClient } from '@hcengineering/account-client'
|
||||
import { LocalUrl, PlatformAdmin } from '../utils'
|
||||
|
||||
let adminAccountClient: AccountClient
|
||||
|
||||
export async function getAdminAccountClient (): Promise<AccountClient> {
|
||||
if (adminAccountClient != null) {
|
||||
return adminAccountClient
|
||||
}
|
||||
|
||||
const unauthClient = getClientRaw(LocalUrl)
|
||||
const loginInfo = await unauthClient.login(PlatformAdmin, '1234')
|
||||
|
||||
if (loginInfo == null) {
|
||||
throw new Error('Failed to login as admin')
|
||||
}
|
||||
|
||||
adminAccountClient = getClientRaw(LocalUrl, loginInfo.token)
|
||||
return adminAccountClient
|
||||
}
|
252
tests/sanity/tests/integrations.spec.ts
Normal file
252
tests/sanity/tests/integrations.spec.ts
Normal file
@ -0,0 +1,252 @@
|
||||
import { expect, test } from '@playwright/test'
|
||||
import { faker } from '@faker-js/faker'
|
||||
import { Integration, IntegrationSecret } from '@hcengineering/account'
|
||||
import { buildSocialIdString, SocialIdType } from '@hcengineering/core'
|
||||
|
||||
import { PlatformUser } from './utils'
|
||||
import { getAdminAccountClient } from './API/AccountClient'
|
||||
|
||||
test.describe('integrations in accounts tests', () => {
|
||||
test('manage integrations', async () => {
|
||||
const accountClient = await getAdminAccountClient()
|
||||
|
||||
const personUuid = await accountClient.findPersonBySocialKey(
|
||||
buildSocialIdString({ type: SocialIdType.EMAIL, value: PlatformUser })
|
||||
)
|
||||
if (personUuid == null) {
|
||||
throw new Error('Failed to find person for PlatformUser: ' + PlatformUser)
|
||||
}
|
||||
|
||||
const personId1 = await accountClient.addSocialIdToPerson(personUuid, SocialIdType.EMAIL, faker.word.words(1), true)
|
||||
const personId2 = await accountClient.addSocialIdToPerson(
|
||||
personUuid,
|
||||
SocialIdType.GITHUB,
|
||||
faker.word.words(1),
|
||||
true
|
||||
)
|
||||
const personId3 = await accountClient.addSocialIdToPerson(
|
||||
personUuid,
|
||||
SocialIdType.GOOGLE,
|
||||
faker.word.words(1),
|
||||
true
|
||||
)
|
||||
const workspaces = await accountClient.listWorkspaces()
|
||||
if (workspaces.length === 0) {
|
||||
throw new Error('No workspaces found')
|
||||
}
|
||||
const workspaceUuid = workspaces[0].uuid
|
||||
|
||||
// Test data setup
|
||||
const integration1: Integration = {
|
||||
socialId: personId1,
|
||||
kind: 'github',
|
||||
workspaceUuid: null, // Global integration
|
||||
data: {
|
||||
repo: 'repo1',
|
||||
owner: 'owner1',
|
||||
branch: 'main'
|
||||
}
|
||||
}
|
||||
|
||||
const integration2: Integration = {
|
||||
socialId: personId2,
|
||||
kind: 'telegram-bot',
|
||||
workspaceUuid,
|
||||
data: {
|
||||
chatId: '123',
|
||||
username: 'bot1',
|
||||
webhookUrl: 'https://example.com/webhook'
|
||||
}
|
||||
}
|
||||
|
||||
const integration3: Integration = {
|
||||
socialId: personId3,
|
||||
kind: 'mailbox',
|
||||
workspaceUuid,
|
||||
data: {
|
||||
email: 'test@example.com',
|
||||
name: 'Test Mailbox',
|
||||
settings: { folder: 'INBOX' }
|
||||
}
|
||||
}
|
||||
|
||||
// Create and verify integrations one by one
|
||||
await accountClient.createIntegration(integration1)
|
||||
const checkIntegration1 = await accountClient.getIntegration({
|
||||
socialId: integration1.socialId,
|
||||
kind: integration1.kind,
|
||||
workspaceUuid: integration1.workspaceUuid
|
||||
})
|
||||
expect(checkIntegration1).toEqual(integration1)
|
||||
|
||||
await accountClient.createIntegration(integration2)
|
||||
let checkIntegration2 = await accountClient.getIntegration({
|
||||
socialId: integration2.socialId,
|
||||
kind: integration2.kind,
|
||||
workspaceUuid: integration2.workspaceUuid
|
||||
})
|
||||
expect(checkIntegration2).toEqual(integration2)
|
||||
|
||||
await accountClient.createIntegration(integration3)
|
||||
const checkIntegration3 = await accountClient.getIntegration({
|
||||
socialId: integration3.socialId,
|
||||
kind: integration3.kind,
|
||||
workspaceUuid: integration3.workspaceUuid
|
||||
})
|
||||
expect(checkIntegration3).toEqual(integration3)
|
||||
|
||||
// Create secrets
|
||||
const secret1: IntegrationSecret = {
|
||||
socialId: personId1,
|
||||
kind: 'github',
|
||||
workspaceUuid: null,
|
||||
key: 'token',
|
||||
secret: 'github_pat_token_123'
|
||||
}
|
||||
|
||||
const secret2: IntegrationSecret = {
|
||||
socialId: personId2,
|
||||
kind: 'telegram-bot',
|
||||
workspaceUuid,
|
||||
key: 'bot_token',
|
||||
secret: 'telegram_bot_token_123'
|
||||
}
|
||||
|
||||
const secret3: IntegrationSecret = {
|
||||
socialId: personId2,
|
||||
kind: 'telegram-bot',
|
||||
workspaceUuid,
|
||||
key: 'api_key',
|
||||
secret: 'telegram_api_key_123'
|
||||
}
|
||||
|
||||
// Add and verify secrets one by one
|
||||
await accountClient.addIntegrationSecret(secret1)
|
||||
const checkSecret1 = await accountClient.getIntegrationSecret({
|
||||
socialId: secret1.socialId,
|
||||
kind: secret1.kind,
|
||||
workspaceUuid: secret1.workspaceUuid,
|
||||
key: secret1.key
|
||||
})
|
||||
expect(checkSecret1).toEqual(secret1)
|
||||
|
||||
await accountClient.addIntegrationSecret(secret2)
|
||||
let checkSecret2 = await accountClient.getIntegrationSecret({
|
||||
socialId: secret2.socialId,
|
||||
kind: secret2.kind,
|
||||
workspaceUuid: secret2.workspaceUuid,
|
||||
key: secret2.key
|
||||
})
|
||||
expect(checkSecret2).toEqual(secret2)
|
||||
|
||||
await accountClient.addIntegrationSecret(secret3)
|
||||
const checkSecret3 = await accountClient.getIntegrationSecret({
|
||||
socialId: secret3.socialId,
|
||||
kind: secret3.kind,
|
||||
workspaceUuid: secret3.workspaceUuid,
|
||||
key: secret3.key
|
||||
})
|
||||
expect(checkSecret3).toEqual(secret3)
|
||||
|
||||
// Test listing with various filters
|
||||
const allIntegrations = await accountClient.listIntegrations({})
|
||||
expect(allIntegrations).toHaveLength(3)
|
||||
expect(allIntegrations).toEqual(expect.arrayContaining([integration1, integration2, integration3]))
|
||||
|
||||
const workspaceIntegrations = await accountClient.listIntegrations({ workspaceUuid })
|
||||
expect(workspaceIntegrations).toHaveLength(2)
|
||||
expect(workspaceIntegrations).toEqual(expect.arrayContaining([integration2, integration3]))
|
||||
|
||||
const githubIntegrations = await accountClient.listIntegrations({ kind: 'github' })
|
||||
expect(githubIntegrations).toHaveLength(1)
|
||||
expect(githubIntegrations[0]).toEqual(integration1)
|
||||
|
||||
const telegramIntegrations = await accountClient.listIntegrations({ kind: 'telegram-bot' })
|
||||
expect(telegramIntegrations).toHaveLength(1)
|
||||
expect(telegramIntegrations[0]).toEqual(integration2)
|
||||
|
||||
// Test listing secrets with filters
|
||||
const allSecrets = await accountClient.listIntegrationsSecrets({})
|
||||
expect(allSecrets).toHaveLength(3)
|
||||
expect(allSecrets).toEqual(expect.arrayContaining([secret1, secret2, secret3]))
|
||||
|
||||
const workspaceSecrets = await accountClient.listIntegrationsSecrets({
|
||||
workspaceUuid
|
||||
})
|
||||
expect(workspaceSecrets).toHaveLength(2)
|
||||
expect(workspaceSecrets).toEqual(expect.arrayContaining([secret2, secret3]))
|
||||
|
||||
const telegramSecrets = await accountClient.listIntegrationsSecrets({ kind: 'telegram-bot' })
|
||||
expect(telegramSecrets).toHaveLength(2)
|
||||
expect(telegramSecrets).toEqual(expect.arrayContaining([secret2, secret3]))
|
||||
|
||||
// Test updates
|
||||
const updatedIntegration2: Integration = {
|
||||
...integration2,
|
||||
data: {
|
||||
chatId: '456',
|
||||
username: 'bot1_updated',
|
||||
webhookUrl: 'https://example.com/webhook2'
|
||||
}
|
||||
}
|
||||
await accountClient.updateIntegration(updatedIntegration2)
|
||||
checkIntegration2 = await accountClient.getIntegration({
|
||||
socialId: integration2.socialId,
|
||||
kind: integration2.kind,
|
||||
workspaceUuid: integration2.workspaceUuid
|
||||
})
|
||||
expect(checkIntegration2).toEqual(updatedIntegration2)
|
||||
|
||||
const updatedSecret2: IntegrationSecret = {
|
||||
...secret2,
|
||||
secret: 'telegram_bot_token_456'
|
||||
}
|
||||
await accountClient.updateIntegrationSecret(updatedSecret2)
|
||||
checkSecret2 = await accountClient.getIntegrationSecret({
|
||||
socialId: secret2.socialId,
|
||||
kind: secret2.kind,
|
||||
workspaceUuid: secret2.workspaceUuid,
|
||||
key: secret2.key
|
||||
})
|
||||
expect(checkSecret2).toEqual(updatedSecret2)
|
||||
|
||||
// Test deletions
|
||||
await accountClient.deleteIntegration({
|
||||
socialId: integration1.socialId,
|
||||
kind: integration1.kind,
|
||||
workspaceUuid: integration1.workspaceUuid
|
||||
})
|
||||
|
||||
await accountClient.deleteIntegrationSecret({
|
||||
socialId: secret2.socialId,
|
||||
kind: secret2.kind,
|
||||
workspaceUuid: secret2.workspaceUuid,
|
||||
key: secret2.key
|
||||
})
|
||||
|
||||
// Verify deletions
|
||||
const remainingIntegrations = await accountClient.listIntegrations({})
|
||||
expect(remainingIntegrations).toHaveLength(2)
|
||||
expect(remainingIntegrations).toEqual(expect.arrayContaining([updatedIntegration2, integration3]))
|
||||
|
||||
const remainingSecrets = await accountClient.listIntegrationsSecrets({})
|
||||
expect(remainingSecrets).toHaveLength(1)
|
||||
expect(remainingSecrets).toEqual(expect.arrayContaining([secret3]))
|
||||
|
||||
// Verify deleted items don't exist
|
||||
const deletedIntegration = await accountClient.getIntegration({
|
||||
socialId: integration1.socialId,
|
||||
kind: integration1.kind,
|
||||
workspaceUuid: integration1.workspaceUuid
|
||||
})
|
||||
expect(deletedIntegration).toBeNull()
|
||||
|
||||
const deletedSecret = await accountClient.getIntegrationSecret({
|
||||
socialId: secret2.socialId,
|
||||
kind: secret2.kind,
|
||||
workspaceUuid: secret2.workspaceUuid,
|
||||
key: secret2.key
|
||||
})
|
||||
expect(deletedSecret).toBeNull()
|
||||
})
|
||||
})
|
@ -14,6 +14,7 @@ export const PlatformURI = process.env.PLATFORM_URI as string
|
||||
export const PlatformTransactor = process.env.PLATFORM_TRANSACTOR as string
|
||||
export const PlatformUser = process.env.PLATFORM_USER as string
|
||||
export const PlatformUserSecond = process.env.PLATFORM_USER_SECOND as string
|
||||
export const PlatformAdmin = process.env.PLATFORM_ADMIN as string
|
||||
export const PlatformWs = process.env.PLATFORM_WS as string
|
||||
export const PlatformSetting = process.env.SETTING as string
|
||||
export const PlatformSettingSecond = process.env.SETTING_SECOND as string
|
||||
|
@ -32,7 +32,7 @@ services:
|
||||
- 27018:27018
|
||||
restart: unless-stopped
|
||||
cockroach:
|
||||
image: cockroachdb/cockroach:latest-v24.2
|
||||
image: cockroachdb/cockroach:v24.1.2
|
||||
extra_hosts:
|
||||
- 'huly.local:host-gateway'
|
||||
ports:
|
||||
|
Loading…
Reference in New Issue
Block a user