mirror of
https://github.com/different-ai/openwork
synced 2026-04-25 17:15:34 +02:00
Draft plugin architecture and connector docs (#1474)
* docs: outline plugin architecture and connector design * feat(den-api): add plugin architecture admin and webhook foundation * refactor(den-api): rename plugin routes and split github connector env * fix(den-api): scope plugin connector records by organization --------- Co-authored-by: src-opn <src-opn@users.noreply.github.com>
This commit is contained in:
@@ -16,6 +16,7 @@ import { registerAuthRoutes } from "./routes/auth/index.js"
|
||||
import { registerMeRoutes } from "./routes/me/index.js"
|
||||
import { registerOrgRoutes } from "./routes/org/index.js"
|
||||
import { registerVersionRoutes } from "./routes/version/index.js"
|
||||
import { registerWebhookRoutes } from "./routes/webhooks/index.js"
|
||||
import { registerWorkerRoutes } from "./routes/workers/index.js"
|
||||
import type { AuthContextVariables } from "./session.js"
|
||||
import { sessionMiddleware } from "./session.js"
|
||||
@@ -108,6 +109,7 @@ registerAuthRoutes(app)
|
||||
registerMeRoutes(app)
|
||||
registerOrgRoutes(app)
|
||||
registerVersionRoutes(app)
|
||||
registerWebhookRoutes(app)
|
||||
registerWorkerRoutes(app)
|
||||
|
||||
app.get(
|
||||
|
||||
@@ -13,6 +13,11 @@ const EnvSchema = z.object({
|
||||
DEN_BETTER_AUTH_TRUSTED_ORIGINS: z.string().optional(),
|
||||
GITHUB_CLIENT_ID: z.string().optional(),
|
||||
GITHUB_CLIENT_SECRET: z.string().optional(),
|
||||
GITHUB_CONNECTOR_APP_ID: z.string().optional(),
|
||||
GITHUB_CONNECTOR_APP_CLIENT_ID: z.string().optional(),
|
||||
GITHUB_CONNECTOR_APP_CLIENT_SECRET: z.string().optional(),
|
||||
GITHUB_CONNECTOR_APP_PRIVATE_KEY: z.string().optional(),
|
||||
GITHUB_CONNECTOR_APP_WEBHOOK_SECRET: z.string().optional(),
|
||||
GOOGLE_CLIENT_ID: z.string().optional(),
|
||||
GOOGLE_CLIENT_SECRET: z.string().optional(),
|
||||
LOOPS_API_KEY: z.string().optional(),
|
||||
@@ -158,6 +163,13 @@ export const env = {
|
||||
clientId: optionalString(parsed.GITHUB_CLIENT_ID),
|
||||
clientSecret: optionalString(parsed.GITHUB_CLIENT_SECRET),
|
||||
},
|
||||
githubConnectorApp: {
|
||||
appId: optionalString(parsed.GITHUB_CONNECTOR_APP_ID),
|
||||
clientId: optionalString(parsed.GITHUB_CONNECTOR_APP_CLIENT_ID),
|
||||
clientSecret: optionalString(parsed.GITHUB_CONNECTOR_APP_CLIENT_SECRET),
|
||||
privateKey: optionalString(parsed.GITHUB_CONNECTOR_APP_PRIVATE_KEY),
|
||||
webhookSecret: optionalString(parsed.GITHUB_CONNECTOR_APP_WEBHOOK_SECRET),
|
||||
},
|
||||
google: {
|
||||
clientId: optionalString(parsed.GOOGLE_CLIENT_ID),
|
||||
clientSecret: optionalString(parsed.GOOGLE_CLIENT_SECRET),
|
||||
|
||||
@@ -5,6 +5,7 @@ import { registerOrgCoreRoutes } from "./core.js"
|
||||
import { registerOrgInvitationRoutes } from "./invitations.js"
|
||||
import { registerOrgLlmProviderRoutes } from "./llm-providers.js"
|
||||
import { registerOrgMemberRoutes } from "./members.js"
|
||||
import { registerPluginArchRoutes } from "./plugin-system/routes.js"
|
||||
import { registerOrgRoleRoutes } from "./roles.js"
|
||||
import { registerOrgSkillRoutes } from "./skills.js"
|
||||
import { registerOrgTeamRoutes } from "./teams.js"
|
||||
@@ -16,6 +17,7 @@ export function registerOrgRoutes<T extends { Variables: OrgRouteVariables }>(ap
|
||||
registerOrgInvitationRoutes(app)
|
||||
registerOrgLlmProviderRoutes(app)
|
||||
registerOrgMemberRoutes(app)
|
||||
registerPluginArchRoutes(app)
|
||||
registerOrgRoleRoutes(app)
|
||||
registerOrgSkillRoutes(app)
|
||||
registerOrgTeamRoutes(app)
|
||||
|
||||
230
ee/apps/den-api/src/routes/org/plugin-system/access.ts
Normal file
230
ee/apps/den-api/src/routes/org/plugin-system/access.ts
Normal file
@@ -0,0 +1,230 @@
|
||||
import { and, eq, inArray, isNull } from "@openwork-ee/den-db/drizzle"
|
||||
import {
|
||||
ConfigObjectAccessGrantTable,
|
||||
ConfigObjectTable,
|
||||
ConnectorInstanceAccessGrantTable,
|
||||
ConnectorInstanceTable,
|
||||
PluginAccessGrantTable,
|
||||
PluginConfigObjectTable,
|
||||
PluginTable,
|
||||
} from "@openwork-ee/den-db/schema"
|
||||
import type { MemberTeamSummary, OrganizationContext } from "../../../orgs.js"
|
||||
import { db } from "../../../db.js"
|
||||
import { memberHasRole } from "../shared.js"
|
||||
|
||||
export type PluginArchResourceKind = "config_object" | "connector_instance" | "plugin"
|
||||
export type PluginArchRole = "viewer" | "editor" | "manager"
|
||||
export type PluginArchCapability = "config_object.create" | "connector_account.create" | "connector_instance.create" | "plugin.create"
|
||||
|
||||
export type PluginArchActorContext = {
|
||||
memberTeams: MemberTeamSummary[]
|
||||
organizationContext: OrganizationContext
|
||||
}
|
||||
|
||||
type MemberId = OrganizationContext["currentMember"]["id"]
|
||||
type TeamId = MemberTeamSummary["id"]
|
||||
type ConfigObjectId = typeof ConfigObjectTable.$inferSelect.id
|
||||
type PluginId = typeof PluginTable.$inferSelect.id
|
||||
type ConnectorInstanceId = typeof ConnectorInstanceTable.$inferSelect.id
|
||||
type ConfigObjectGrantRow = Pick<typeof ConfigObjectAccessGrantTable.$inferSelect, "orgMembershipId" | "orgWide" | "removedAt" | "role" | "teamId">
|
||||
type PluginGrantRow = Pick<typeof PluginAccessGrantTable.$inferSelect, "orgMembershipId" | "orgWide" | "removedAt" | "role" | "teamId">
|
||||
type ConnectorInstanceGrantRow = Pick<typeof ConnectorInstanceAccessGrantTable.$inferSelect, "orgMembershipId" | "orgWide" | "removedAt" | "role" | "teamId">
|
||||
type GrantRow = ConfigObjectGrantRow | PluginGrantRow | ConnectorInstanceGrantRow
|
||||
|
||||
type PluginResourceLookupInput = {
|
||||
context: PluginArchActorContext
|
||||
resourceId: PluginId
|
||||
resourceKind: "plugin"
|
||||
}
|
||||
|
||||
type ConnectorInstanceResourceLookupInput = {
|
||||
context: PluginArchActorContext
|
||||
resourceId: ConnectorInstanceId
|
||||
resourceKind: "connector_instance"
|
||||
}
|
||||
|
||||
type ConfigObjectResourceLookupInput = {
|
||||
context: PluginArchActorContext
|
||||
resourceId: ConfigObjectId
|
||||
resourceKind: "config_object"
|
||||
}
|
||||
|
||||
type ResourceLookupInput =
|
||||
| PluginResourceLookupInput
|
||||
| ConnectorInstanceResourceLookupInput
|
||||
| ConfigObjectResourceLookupInput
|
||||
|
||||
type RequireResourceRoleInput = ResourceLookupInput & { role: PluginArchRole }
|
||||
|
||||
export class PluginArchAuthorizationError extends Error {
|
||||
constructor(
|
||||
readonly status: 403,
|
||||
readonly error: "forbidden",
|
||||
message: string,
|
||||
) {
|
||||
super(message)
|
||||
this.name = "PluginArchAuthorizationError"
|
||||
}
|
||||
}
|
||||
|
||||
const rolePriority: Record<PluginArchRole, number> = {
|
||||
viewer: 1,
|
||||
editor: 2,
|
||||
manager: 3,
|
||||
}
|
||||
|
||||
function maxRole(current: PluginArchRole | null, candidate: PluginArchRole | null) {
|
||||
if (!candidate) return current
|
||||
if (!current) return candidate
|
||||
return rolePriority[candidate] > rolePriority[current] ? candidate : current
|
||||
}
|
||||
|
||||
export function isPluginArchOrgAdmin(context: PluginArchActorContext) {
|
||||
return context.organizationContext.currentMember.isOwner || memberHasRole(context.organizationContext.currentMember.role, "admin")
|
||||
}
|
||||
|
||||
export function hasPluginArchCapability(context: PluginArchActorContext, _capability: PluginArchCapability) {
|
||||
return isPluginArchOrgAdmin(context)
|
||||
}
|
||||
|
||||
function roleSatisfies(role: PluginArchRole | null, required: PluginArchRole) {
|
||||
if (!role) return false
|
||||
return rolePriority[role] >= rolePriority[required]
|
||||
}
|
||||
|
||||
export function resolvePluginArchGrantRole(input: {
|
||||
grants: GrantRow[]
|
||||
memberId: MemberId
|
||||
teamIds: TeamId[]
|
||||
}) {
|
||||
const teamIds = new Set(input.teamIds)
|
||||
let resolved: PluginArchRole | null = null
|
||||
|
||||
for (const grant of input.grants) {
|
||||
if (grant.removedAt) continue
|
||||
const applies = grant.orgWide || grant.orgMembershipId === input.memberId || (grant.teamId ? teamIds.has(grant.teamId) : false)
|
||||
if (!applies) continue
|
||||
resolved = maxRole(resolved, grant.role)
|
||||
}
|
||||
|
||||
return resolved
|
||||
}
|
||||
|
||||
async function resolveGrantRole(input: {
|
||||
grants: GrantRow[]
|
||||
context: PluginArchActorContext
|
||||
}) {
|
||||
return resolvePluginArchGrantRole({
|
||||
grants: input.grants,
|
||||
memberId: input.context.organizationContext.currentMember.id,
|
||||
teamIds: input.context.memberTeams.map((team) => team.id),
|
||||
})
|
||||
}
|
||||
|
||||
async function resolvePluginRoleForIds(context: PluginArchActorContext, pluginIds: PluginId[]) {
|
||||
if (pluginIds.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (isPluginArchOrgAdmin(context)) {
|
||||
return "manager" satisfies PluginArchRole
|
||||
}
|
||||
|
||||
const grants = await db
|
||||
.select({
|
||||
orgMembershipId: PluginAccessGrantTable.orgMembershipId,
|
||||
orgWide: PluginAccessGrantTable.orgWide,
|
||||
removedAt: PluginAccessGrantTable.removedAt,
|
||||
role: PluginAccessGrantTable.role,
|
||||
teamId: PluginAccessGrantTable.teamId,
|
||||
})
|
||||
.from(PluginAccessGrantTable)
|
||||
.where(inArray(PluginAccessGrantTable.pluginId, pluginIds))
|
||||
|
||||
return resolveGrantRole({ context, grants })
|
||||
}
|
||||
|
||||
export async function resolvePluginArchResourceRole(input: ResourceLookupInput) {
|
||||
if (isPluginArchOrgAdmin(input.context)) {
|
||||
return "manager" satisfies PluginArchRole
|
||||
}
|
||||
|
||||
if (input.resourceKind === "plugin") {
|
||||
const grants = await db
|
||||
.select({
|
||||
orgMembershipId: PluginAccessGrantTable.orgMembershipId,
|
||||
orgWide: PluginAccessGrantTable.orgWide,
|
||||
removedAt: PluginAccessGrantTable.removedAt,
|
||||
role: PluginAccessGrantTable.role,
|
||||
teamId: PluginAccessGrantTable.teamId,
|
||||
})
|
||||
.from(PluginAccessGrantTable)
|
||||
.where(eq(PluginAccessGrantTable.pluginId, input.resourceId))
|
||||
return resolveGrantRole({ context: input.context, grants })
|
||||
}
|
||||
|
||||
if (input.resourceKind === "connector_instance") {
|
||||
const grants = await db
|
||||
.select({
|
||||
orgMembershipId: ConnectorInstanceAccessGrantTable.orgMembershipId,
|
||||
orgWide: ConnectorInstanceAccessGrantTable.orgWide,
|
||||
removedAt: ConnectorInstanceAccessGrantTable.removedAt,
|
||||
role: ConnectorInstanceAccessGrantTable.role,
|
||||
teamId: ConnectorInstanceAccessGrantTable.teamId,
|
||||
})
|
||||
.from(ConnectorInstanceAccessGrantTable)
|
||||
.where(eq(ConnectorInstanceAccessGrantTable.connectorInstanceId, input.resourceId))
|
||||
return resolveGrantRole({ context: input.context, grants })
|
||||
}
|
||||
|
||||
const directGrants = await db
|
||||
.select({
|
||||
orgMembershipId: ConfigObjectAccessGrantTable.orgMembershipId,
|
||||
orgWide: ConfigObjectAccessGrantTable.orgWide,
|
||||
removedAt: ConfigObjectAccessGrantTable.removedAt,
|
||||
role: ConfigObjectAccessGrantTable.role,
|
||||
teamId: ConfigObjectAccessGrantTable.teamId,
|
||||
})
|
||||
.from(ConfigObjectAccessGrantTable)
|
||||
.where(eq(ConfigObjectAccessGrantTable.configObjectId, input.resourceId))
|
||||
|
||||
let resolved = await resolveGrantRole({ context: input.context, grants: directGrants })
|
||||
if (resolved) {
|
||||
return resolved
|
||||
}
|
||||
|
||||
const memberships = await db
|
||||
.select({ pluginId: PluginConfigObjectTable.pluginId })
|
||||
.from(PluginConfigObjectTable)
|
||||
.where(and(eq(PluginConfigObjectTable.configObjectId, input.resourceId), isNull(PluginConfigObjectTable.removedAt)))
|
||||
|
||||
const pluginRole = await resolvePluginRoleForIds(input.context, memberships.map((membership) => membership.pluginId))
|
||||
resolved = maxRole(resolved, pluginRole ? "viewer" : null)
|
||||
return resolved
|
||||
}
|
||||
|
||||
export async function requirePluginArchCapability(context: PluginArchActorContext, capability: PluginArchCapability) {
|
||||
if (hasPluginArchCapability(context, capability)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw new PluginArchAuthorizationError(403, "forbidden", `Missing organization capability: ${capability}`)
|
||||
}
|
||||
|
||||
export async function requirePluginArchResourceRole(input: {
|
||||
context: PluginArchActorContext
|
||||
resourceId: ConfigObjectId | ConnectorInstanceId | PluginId
|
||||
resourceKind: PluginArchResourceKind
|
||||
role: PluginArchRole
|
||||
}) {
|
||||
const resolved = await resolvePluginArchResourceRole(input as RequireResourceRoleInput)
|
||||
if (roleSatisfies(resolved, input.role)) {
|
||||
return resolved
|
||||
}
|
||||
|
||||
throw new PluginArchAuthorizationError(
|
||||
403,
|
||||
"forbidden",
|
||||
`Missing ${input.role} access for ${input.resourceKind.replace(/_/g, " ")}.`,
|
||||
)
|
||||
}
|
||||
781
ee/apps/den-api/src/routes/org/plugin-system/contracts.ts
Normal file
781
ee/apps/den-api/src/routes/org/plugin-system/contracts.ts
Normal file
@@ -0,0 +1,781 @@
|
||||
import type { z } from "zod"
|
||||
import {
|
||||
accessGrantListResponseSchema,
|
||||
accessGrantMutationResponseSchema,
|
||||
configObjectAccessGrantParamsSchema,
|
||||
configObjectCreateSchema,
|
||||
configObjectCreateVersionSchema,
|
||||
configObjectDetailResponseSchema,
|
||||
configObjectListQuerySchema,
|
||||
configObjectListResponseSchema,
|
||||
configObjectMutationResponseSchema,
|
||||
configObjectParamsSchema,
|
||||
configObjectPluginAttachSchema,
|
||||
configObjectVersionDetailResponseSchema,
|
||||
configObjectVersionListQuerySchema,
|
||||
configObjectVersionListResponseSchema,
|
||||
configObjectVersionParamsSchema,
|
||||
connectorAccountCreateSchema,
|
||||
connectorAccountDetailResponseSchema,
|
||||
connectorAccountDisconnectSchema,
|
||||
connectorAccountListQuerySchema,
|
||||
connectorAccountListResponseSchema,
|
||||
connectorAccountMutationResponseSchema,
|
||||
connectorAccountParamsSchema,
|
||||
connectorAccountRepositoryParamsSchema,
|
||||
connectorInstanceAccessGrantParamsSchema,
|
||||
connectorInstanceCreateSchema,
|
||||
connectorInstanceDetailResponseSchema,
|
||||
connectorInstanceListQuerySchema,
|
||||
connectorInstanceListResponseSchema,
|
||||
connectorInstanceMutationResponseSchema,
|
||||
connectorInstanceParamsSchema,
|
||||
connectorInstanceUpdateSchema,
|
||||
connectorMappingCreateSchema,
|
||||
connectorMappingListQuerySchema,
|
||||
connectorMappingListResponseSchema,
|
||||
connectorMappingMutationResponseSchema,
|
||||
connectorMappingParamsSchema,
|
||||
connectorMappingUpdateSchema,
|
||||
connectorSyncAsyncResponseSchema,
|
||||
connectorSyncEventDetailResponseSchema,
|
||||
connectorSyncEventListQuerySchema,
|
||||
connectorSyncEventListResponseSchema,
|
||||
connectorSyncEventParamsSchema,
|
||||
connectorTargetCreateSchema,
|
||||
connectorTargetDetailResponseSchema,
|
||||
connectorTargetListQuerySchema,
|
||||
connectorTargetListResponseSchema,
|
||||
connectorTargetMutationResponseSchema,
|
||||
connectorTargetParamsSchema,
|
||||
connectorTargetUpdateSchema,
|
||||
githubConnectorAccountCreateSchema,
|
||||
githubConnectorSetupSchema,
|
||||
githubRepositoryListQuerySchema,
|
||||
githubRepositoryListResponseSchema,
|
||||
githubSetupResponseSchema,
|
||||
githubValidateTargetResponseSchema,
|
||||
githubValidateTargetSchema,
|
||||
githubWebhookAcceptedResponseSchema,
|
||||
githubWebhookHeadersSchema,
|
||||
githubWebhookIgnoredResponseSchema,
|
||||
githubWebhookRawBodySchema,
|
||||
githubWebhookUnauthorizedResponseSchema,
|
||||
pluginAccessGrantParamsSchema,
|
||||
pluginConfigObjectParamsSchema,
|
||||
pluginCreateSchema,
|
||||
pluginDetailResponseSchema,
|
||||
pluginListQuerySchema,
|
||||
pluginListResponseSchema,
|
||||
pluginMembershipMutationResponseSchema,
|
||||
pluginMembershipListResponseSchema,
|
||||
pluginMembershipWriteSchema,
|
||||
pluginMutationResponseSchema,
|
||||
pluginParamsSchema,
|
||||
pluginUpdateSchema,
|
||||
resourceAccessGrantWriteSchema,
|
||||
} from "./schemas.js"
|
||||
import { orgIdParamSchema } from "../shared.js"
|
||||
|
||||
type EndpointMethod = "DELETE" | "GET" | "PATCH" | "POST"
|
||||
type EndpointAudience = "admin" | "public_webhook"
|
||||
type EndpointTag = "Config Objects" | "Plugins" | "Connectors" | "GitHub" | "Webhooks"
|
||||
|
||||
type EndpointContract = {
|
||||
audience: EndpointAudience
|
||||
description: string
|
||||
method: EndpointMethod
|
||||
path: string
|
||||
request?: {
|
||||
body?: z.ZodTypeAny
|
||||
headers?: z.ZodTypeAny
|
||||
params?: z.ZodTypeAny
|
||||
query?: z.ZodTypeAny
|
||||
}
|
||||
response: {
|
||||
description: string
|
||||
schema?: z.ZodTypeAny
|
||||
status: 200 | 201 | 202 | 204 | 401
|
||||
}
|
||||
tag: EndpointTag
|
||||
}
|
||||
|
||||
type DeferredEndpointContract = {
|
||||
description: string
|
||||
method: EndpointMethod
|
||||
path: string
|
||||
reason: string
|
||||
tag: EndpointTag
|
||||
}
|
||||
|
||||
const orgBasePath = "/v1/orgs/:orgId"
|
||||
|
||||
export const pluginArchRoutePaths = {
|
||||
configObjects: `${orgBasePath}/config-objects`,
|
||||
configObject: `${orgBasePath}/config-objects/:configObjectId`,
|
||||
configObjectArchive: `${orgBasePath}/config-objects/:configObjectId/archive`,
|
||||
configObjectDelete: `${orgBasePath}/config-objects/:configObjectId/delete`,
|
||||
configObjectRestore: `${orgBasePath}/config-objects/:configObjectId/restore`,
|
||||
configObjectPlugins: `${orgBasePath}/config-objects/:configObjectId/plugins`,
|
||||
configObjectPlugin: `${orgBasePath}/config-objects/:configObjectId/plugins/:pluginId`,
|
||||
configObjectAccess: `${orgBasePath}/config-objects/:configObjectId/access`,
|
||||
configObjectAccessGrant: `${orgBasePath}/config-objects/:configObjectId/access/:grantId`,
|
||||
configObjectVersions: `${orgBasePath}/config-objects/:configObjectId/versions`,
|
||||
configObjectVersion: `${orgBasePath}/config-objects/:configObjectId/versions/:versionId`,
|
||||
configObjectLatestVersion: `${orgBasePath}/config-objects/:configObjectId/versions/latest`,
|
||||
configObjectCompareVersions: `${orgBasePath}/config-objects/:configObjectId/versions/compare`,
|
||||
skills: `${orgBasePath}/skills`,
|
||||
agents: `${orgBasePath}/agents`,
|
||||
commands: `${orgBasePath}/commands`,
|
||||
tools: `${orgBasePath}/tools`,
|
||||
mcps: `${orgBasePath}/mcps`,
|
||||
plugins: `${orgBasePath}/plugins`,
|
||||
plugin: `${orgBasePath}/plugins/:pluginId`,
|
||||
pluginArchive: `${orgBasePath}/plugins/:pluginId/archive`,
|
||||
pluginRestore: `${orgBasePath}/plugins/:pluginId/restore`,
|
||||
pluginConfigObjects: `${orgBasePath}/plugins/:pluginId/config-objects`,
|
||||
pluginConfigObject: `${orgBasePath}/plugins/:pluginId/config-objects/:configObjectId`,
|
||||
pluginResolved: `${orgBasePath}/plugins/:pluginId/resolved`,
|
||||
pluginReleases: `${orgBasePath}/plugins/:pluginId/releases`,
|
||||
pluginAccess: `${orgBasePath}/plugins/:pluginId/access`,
|
||||
pluginAccessGrant: `${orgBasePath}/plugins/:pluginId/access/:grantId`,
|
||||
connectorAccounts: `${orgBasePath}/connector-accounts`,
|
||||
connectorAccount: `${orgBasePath}/connector-accounts/:connectorAccountId`,
|
||||
connectorAccountDisconnect: `${orgBasePath}/connector-accounts/:connectorAccountId/disconnect`,
|
||||
connectorInstances: `${orgBasePath}/connector-instances`,
|
||||
connectorInstance: `${orgBasePath}/connector-instances/:connectorInstanceId`,
|
||||
connectorInstanceArchive: `${orgBasePath}/connector-instances/:connectorInstanceId/archive`,
|
||||
connectorInstanceDisable: `${orgBasePath}/connector-instances/:connectorInstanceId/disable`,
|
||||
connectorInstanceEnable: `${orgBasePath}/connector-instances/:connectorInstanceId/enable`,
|
||||
connectorInstanceAccess: `${orgBasePath}/connector-instances/:connectorInstanceId/access`,
|
||||
connectorInstanceAccessGrant: `${orgBasePath}/connector-instances/:connectorInstanceId/access/:grantId`,
|
||||
connectorTargets: `${orgBasePath}/connector-instances/:connectorInstanceId/targets`,
|
||||
connectorTarget: `${orgBasePath}/connector-targets/:connectorTargetId`,
|
||||
connectorTargetResync: `${orgBasePath}/connector-targets/:connectorTargetId/resync`,
|
||||
connectorTargetMappings: `${orgBasePath}/connector-targets/:connectorTargetId/mappings`,
|
||||
connectorMapping: `${orgBasePath}/connector-mappings/:connectorMappingId`,
|
||||
connectorMappingPreview: `${orgBasePath}/connector-mappings/:connectorMappingId/preview`,
|
||||
connectorSyncEvents: `${orgBasePath}/connector-sync-events`,
|
||||
connectorSyncEvent: `${orgBasePath}/connector-sync-events/:connectorSyncEventId`,
|
||||
connectorSyncEventRetry: `${orgBasePath}/connector-sync-events/:connectorSyncEventId/retry`,
|
||||
githubSetup: `${orgBasePath}/connectors/github/setup`,
|
||||
githubAccounts: `${orgBasePath}/connectors/github/accounts`,
|
||||
githubAccountRepositories: `${orgBasePath}/connectors/github/accounts/:connectorAccountId/repositories`,
|
||||
githubValidateTarget: `${orgBasePath}/connectors/github/validate-target`,
|
||||
githubWebhookIngress: "/api/webhooks/connectors/github",
|
||||
} as const
|
||||
|
||||
export const pluginArchEndpointContracts: Record<string, EndpointContract> = {
|
||||
listConfigObjects: {
|
||||
audience: "admin",
|
||||
description: "List current config object projections with search and connector filters.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.configObjects,
|
||||
request: { params: orgIdParamSchema, query: configObjectListQuerySchema },
|
||||
response: { description: "Current config object rows.", schema: configObjectListResponseSchema, status: 200 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
getConfigObject: {
|
||||
audience: "admin",
|
||||
description: "Get one config object with its latest version projection.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.configObject,
|
||||
request: { params: configObjectParamsSchema },
|
||||
response: { description: "Current config object detail.", schema: configObjectDetailResponseSchema, status: 200 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
createConfigObject: {
|
||||
audience: "admin",
|
||||
description: "Create a cloud or imported config object and optionally attach it to plugins.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.configObjects,
|
||||
request: { body: configObjectCreateSchema, params: orgIdParamSchema },
|
||||
response: { description: "Config object created successfully.", schema: configObjectMutationResponseSchema, status: 201 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
createConfigObjectVersion: {
|
||||
audience: "admin",
|
||||
description: "Create a new immutable version for an existing config object.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.configObjectVersions,
|
||||
request: { body: configObjectCreateVersionSchema, params: configObjectParamsSchema },
|
||||
response: { description: "Latest config object detail after version creation.", schema: configObjectMutationResponseSchema, status: 201 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
archiveConfigObject: {
|
||||
audience: "admin",
|
||||
description: "Archive a config object without removing history.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.configObjectArchive,
|
||||
request: { params: configObjectParamsSchema },
|
||||
response: { description: "Archived config object detail.", schema: configObjectMutationResponseSchema, status: 200 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
deleteConfigObject: {
|
||||
audience: "admin",
|
||||
description: "Soft-delete a config object while preserving history.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.configObjectDelete,
|
||||
request: { params: configObjectParamsSchema },
|
||||
response: { description: "Deleted config object detail.", schema: configObjectMutationResponseSchema, status: 200 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
restoreConfigObject: {
|
||||
audience: "admin",
|
||||
description: "Restore a deleted or archived config object.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.configObjectRestore,
|
||||
request: { params: configObjectParamsSchema },
|
||||
response: { description: "Restored config object detail.", schema: configObjectMutationResponseSchema, status: 200 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
listConfigObjectPlugins: {
|
||||
audience: "admin",
|
||||
description: "List the plugins that currently include a config object.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.configObjectPlugins,
|
||||
request: { params: configObjectParamsSchema },
|
||||
response: { description: "Plugin memberships for the config object.", schema: pluginMembershipListResponseSchema, status: 200 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
attachConfigObjectToPlugin: {
|
||||
audience: "admin",
|
||||
description: "Attach a config object to a plugin using plugin-scoped write access.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.configObjectPlugins,
|
||||
request: { body: configObjectPluginAttachSchema, params: configObjectParamsSchema },
|
||||
response: { description: "Plugin membership created successfully.", schema: pluginMembershipMutationResponseSchema, status: 201 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
removeConfigObjectFromPlugin: {
|
||||
audience: "admin",
|
||||
description: "Remove one active plugin membership from a config object.",
|
||||
method: "DELETE",
|
||||
path: pluginArchRoutePaths.configObjectPlugin,
|
||||
request: { params: configObjectParamsSchema.extend({ pluginId: pluginParamsSchema.shape.pluginId }) },
|
||||
response: { description: "Plugin membership removed successfully.", status: 204 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
listConfigObjectAccess: {
|
||||
audience: "admin",
|
||||
description: "List direct, team, and org-wide grants for a config object.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.configObjectAccess,
|
||||
request: { params: configObjectParamsSchema },
|
||||
response: { description: "Config object access grants.", schema: accessGrantListResponseSchema, status: 200 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
grantConfigObjectAccess: {
|
||||
audience: "admin",
|
||||
description: "Create one direct, team, or org-wide access grant for a config object.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.configObjectAccess,
|
||||
request: { body: resourceAccessGrantWriteSchema, params: configObjectParamsSchema },
|
||||
response: { description: "Config object access grant created successfully.", schema: accessGrantMutationResponseSchema, status: 201 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
revokeConfigObjectAccess: {
|
||||
audience: "admin",
|
||||
description: "Soft-revoke one config object access grant.",
|
||||
method: "DELETE",
|
||||
path: pluginArchRoutePaths.configObjectAccessGrant,
|
||||
request: { params: configObjectAccessGrantParamsSchema },
|
||||
response: { description: "Config object access grant revoked successfully.", status: 204 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
listConfigObjectVersions: {
|
||||
audience: "admin",
|
||||
description: "List immutable versions for a config object.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.configObjectVersions,
|
||||
request: { params: configObjectParamsSchema, query: configObjectVersionListQuerySchema },
|
||||
response: { description: "Config object versions.", schema: configObjectVersionListResponseSchema, status: 200 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
getConfigObjectVersion: {
|
||||
audience: "admin",
|
||||
description: "Get one immutable config object version.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.configObjectVersion,
|
||||
request: { params: configObjectVersionParamsSchema },
|
||||
response: { description: "Config object version detail.", schema: configObjectVersionDetailResponseSchema, status: 200 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
getLatestConfigObjectVersion: {
|
||||
audience: "admin",
|
||||
description: "Resolve the latest config object version using created_at and id ordering.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.configObjectLatestVersion,
|
||||
request: { params: configObjectParamsSchema },
|
||||
response: { description: "Latest config object version detail.", schema: configObjectVersionDetailResponseSchema, status: 200 },
|
||||
tag: "Config Objects",
|
||||
},
|
||||
listPlugins: {
|
||||
audience: "admin",
|
||||
description: "List accessible plugins for the organization.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.plugins,
|
||||
request: { params: orgIdParamSchema, query: pluginListQuerySchema },
|
||||
response: { description: "Plugin list.", schema: pluginListResponseSchema, status: 200 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
getPlugin: {
|
||||
audience: "admin",
|
||||
description: "Get one plugin and its current metadata.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.plugin,
|
||||
request: { params: pluginParamsSchema },
|
||||
response: { description: "Plugin detail.", schema: pluginDetailResponseSchema, status: 200 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
createPlugin: {
|
||||
audience: "admin",
|
||||
description: "Create a private-by-default plugin.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.plugins,
|
||||
request: { body: pluginCreateSchema, params: orgIdParamSchema },
|
||||
response: { description: "Plugin created successfully.", schema: pluginMutationResponseSchema, status: 201 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
updatePlugin: {
|
||||
audience: "admin",
|
||||
description: "Patch plugin metadata.",
|
||||
method: "PATCH",
|
||||
path: pluginArchRoutePaths.plugin,
|
||||
request: { body: pluginUpdateSchema, params: pluginParamsSchema },
|
||||
response: { description: "Plugin updated successfully.", schema: pluginMutationResponseSchema, status: 200 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
archivePlugin: {
|
||||
audience: "admin",
|
||||
description: "Archive a plugin without deleting membership history.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.pluginArchive,
|
||||
request: { params: pluginParamsSchema },
|
||||
response: { description: "Archived plugin detail.", schema: pluginMutationResponseSchema, status: 200 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
restorePlugin: {
|
||||
audience: "admin",
|
||||
description: "Restore an archived or deleted plugin.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.pluginRestore,
|
||||
request: { params: pluginParamsSchema },
|
||||
response: { description: "Restored plugin detail.", schema: pluginMutationResponseSchema, status: 200 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
listPluginConfigObjects: {
|
||||
audience: "admin",
|
||||
description: "List plugin memberships and the current config object projections they reference.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.pluginConfigObjects,
|
||||
request: { params: pluginParamsSchema },
|
||||
response: { description: "Plugin memberships.", schema: pluginMembershipListResponseSchema, status: 200 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
addPluginConfigObject: {
|
||||
audience: "admin",
|
||||
description: "Add a config object to a plugin using plugin-scoped write access.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.pluginConfigObjects,
|
||||
request: { body: pluginMembershipWriteSchema, params: pluginParamsSchema },
|
||||
response: { description: "Plugin membership created successfully.", schema: pluginMembershipMutationResponseSchema, status: 201 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
removePluginConfigObject: {
|
||||
audience: "admin",
|
||||
description: "Remove one config object membership from a plugin.",
|
||||
method: "DELETE",
|
||||
path: pluginArchRoutePaths.pluginConfigObject,
|
||||
request: { params: pluginConfigObjectParamsSchema },
|
||||
response: { description: "Plugin membership removed successfully.", status: 204 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
getResolvedPlugin: {
|
||||
audience: "admin",
|
||||
description: "Preview the resolved latest-version members of a plugin without invoking delivery.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.pluginResolved,
|
||||
request: { params: pluginParamsSchema },
|
||||
response: { description: "Resolved plugin membership view.", schema: pluginMembershipListResponseSchema, status: 200 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
listPluginAccess: {
|
||||
audience: "admin",
|
||||
description: "List direct, team, and org-wide grants for a plugin.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.pluginAccess,
|
||||
request: { params: pluginParamsSchema },
|
||||
response: { description: "Plugin access grants.", schema: accessGrantListResponseSchema, status: 200 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
grantPluginAccess: {
|
||||
audience: "admin",
|
||||
description: "Create one direct, team, or org-wide access grant for a plugin.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.pluginAccess,
|
||||
request: { body: resourceAccessGrantWriteSchema, params: pluginParamsSchema },
|
||||
response: { description: "Plugin access grant created successfully.", schema: accessGrantMutationResponseSchema, status: 201 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
revokePluginAccess: {
|
||||
audience: "admin",
|
||||
description: "Soft-revoke one plugin access grant.",
|
||||
method: "DELETE",
|
||||
path: pluginArchRoutePaths.pluginAccessGrant,
|
||||
request: { params: pluginAccessGrantParamsSchema },
|
||||
response: { description: "Plugin access grant revoked successfully.", status: 204 },
|
||||
tag: "Plugins",
|
||||
},
|
||||
listConnectorAccounts: {
|
||||
audience: "admin",
|
||||
description: "List connector accounts such as GitHub App installations available to the org.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.connectorAccounts,
|
||||
request: { params: orgIdParamSchema, query: connectorAccountListQuerySchema },
|
||||
response: { description: "Connector account list.", schema: connectorAccountListResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
createConnectorAccount: {
|
||||
audience: "admin",
|
||||
description: "Create a reusable connector account record.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.connectorAccounts,
|
||||
request: { body: connectorAccountCreateSchema, params: orgIdParamSchema },
|
||||
response: { description: "Connector account created successfully.", schema: connectorAccountMutationResponseSchema, status: 201 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
getConnectorAccount: {
|
||||
audience: "admin",
|
||||
description: "Get one connector account record.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.connectorAccount,
|
||||
request: { params: connectorAccountParamsSchema },
|
||||
response: { description: "Connector account detail.", schema: connectorAccountDetailResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
disconnectConnectorAccount: {
|
||||
audience: "admin",
|
||||
description: "Disconnect one connector account without deleting historical sync state.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.connectorAccountDisconnect,
|
||||
request: { body: connectorAccountDisconnectSchema, params: connectorAccountParamsSchema },
|
||||
response: { description: "Connector account disconnected successfully.", schema: connectorAccountMutationResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
listConnectorInstances: {
|
||||
audience: "admin",
|
||||
description: "List configured connector instances for the org.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.connectorInstances,
|
||||
request: { params: orgIdParamSchema, query: connectorInstanceListQuerySchema },
|
||||
response: { description: "Connector instance list.", schema: connectorInstanceListResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
createConnectorInstance: {
|
||||
audience: "admin",
|
||||
description: "Create a connector instance backed by one connector account.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.connectorInstances,
|
||||
request: { body: connectorInstanceCreateSchema, params: orgIdParamSchema },
|
||||
response: { description: "Connector instance created successfully.", schema: connectorInstanceMutationResponseSchema, status: 201 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
getConnectorInstance: {
|
||||
audience: "admin",
|
||||
description: "Get one connector instance.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.connectorInstance,
|
||||
request: { params: connectorInstanceParamsSchema },
|
||||
response: { description: "Connector instance detail.", schema: connectorInstanceDetailResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
updateConnectorInstance: {
|
||||
audience: "admin",
|
||||
description: "Patch connector instance metadata or config.",
|
||||
method: "PATCH",
|
||||
path: pluginArchRoutePaths.connectorInstance,
|
||||
request: { body: connectorInstanceUpdateSchema, params: connectorInstanceParamsSchema },
|
||||
response: { description: "Connector instance updated successfully.", schema: connectorInstanceMutationResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
archiveConnectorInstance: {
|
||||
audience: "admin",
|
||||
description: "Archive a connector instance.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.connectorInstanceArchive,
|
||||
request: { params: connectorInstanceParamsSchema },
|
||||
response: { description: "Connector instance archived successfully.", schema: connectorInstanceMutationResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
disableConnectorInstance: {
|
||||
audience: "admin",
|
||||
description: "Disable sync execution for a connector instance.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.connectorInstanceDisable,
|
||||
request: { params: connectorInstanceParamsSchema },
|
||||
response: { description: "Connector instance disabled successfully.", schema: connectorInstanceMutationResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
enableConnectorInstance: {
|
||||
audience: "admin",
|
||||
description: "Re-enable sync execution for a connector instance.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.connectorInstanceEnable,
|
||||
request: { params: connectorInstanceParamsSchema },
|
||||
response: { description: "Connector instance enabled successfully.", schema: connectorInstanceMutationResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
listConnectorInstanceAccess: {
|
||||
audience: "admin",
|
||||
description: "List direct, team, and org-wide grants for a connector instance.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.connectorInstanceAccess,
|
||||
request: { params: connectorInstanceParamsSchema },
|
||||
response: { description: "Connector instance access grants.", schema: accessGrantListResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
grantConnectorInstanceAccess: {
|
||||
audience: "admin",
|
||||
description: "Create one direct, team, or org-wide access grant for a connector instance.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.connectorInstanceAccess,
|
||||
request: { body: resourceAccessGrantWriteSchema, params: connectorInstanceParamsSchema },
|
||||
response: { description: "Connector instance access grant created successfully.", schema: accessGrantMutationResponseSchema, status: 201 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
revokeConnectorInstanceAccess: {
|
||||
audience: "admin",
|
||||
description: "Soft-revoke one connector instance access grant.",
|
||||
method: "DELETE",
|
||||
path: pluginArchRoutePaths.connectorInstanceAccessGrant,
|
||||
request: { params: connectorInstanceAccessGrantParamsSchema },
|
||||
response: { description: "Connector instance access grant revoked successfully.", status: 204 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
listConnectorTargets: {
|
||||
audience: "admin",
|
||||
description: "List external targets configured under a connector instance.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.connectorTargets,
|
||||
request: { params: connectorInstanceParamsSchema, query: connectorTargetListQuerySchema },
|
||||
response: { description: "Connector target list.", schema: connectorTargetListResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
createConnectorTarget: {
|
||||
audience: "admin",
|
||||
description: "Create one connector target such as a GitHub repository branch.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.connectorTargets,
|
||||
request: { body: connectorTargetCreateSchema, params: connectorInstanceParamsSchema },
|
||||
response: { description: "Connector target created successfully.", schema: connectorTargetMutationResponseSchema, status: 201 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
getConnectorTarget: {
|
||||
audience: "admin",
|
||||
description: "Get one connector target.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.connectorTarget,
|
||||
request: { params: connectorTargetParamsSchema },
|
||||
response: { description: "Connector target detail.", schema: connectorTargetDetailResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
updateConnectorTarget: {
|
||||
audience: "admin",
|
||||
description: "Patch one connector target.",
|
||||
method: "PATCH",
|
||||
path: pluginArchRoutePaths.connectorTarget,
|
||||
request: { body: connectorTargetUpdateSchema, params: connectorTargetParamsSchema },
|
||||
response: { description: "Connector target updated successfully.", schema: connectorTargetMutationResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
resyncConnectorTarget: {
|
||||
audience: "admin",
|
||||
description: "Queue a manual reconciliation run for one connector target.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.connectorTargetResync,
|
||||
request: { params: connectorTargetParamsSchema },
|
||||
response: { description: "Connector target resync queued successfully.", schema: connectorSyncAsyncResponseSchema, status: 202 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
listConnectorMappings: {
|
||||
audience: "admin",
|
||||
description: "List mappings configured under a connector target.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.connectorTargetMappings,
|
||||
request: { params: connectorTargetParamsSchema, query: connectorMappingListQuerySchema },
|
||||
response: { description: "Connector mapping list.", schema: connectorMappingListResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
createConnectorMapping: {
|
||||
audience: "admin",
|
||||
description: "Create a path or API mapping for a connector target.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.connectorTargetMappings,
|
||||
request: { body: connectorMappingCreateSchema, params: connectorTargetParamsSchema },
|
||||
response: { description: "Connector mapping created successfully.", schema: connectorMappingMutationResponseSchema, status: 201 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
updateConnectorMapping: {
|
||||
audience: "admin",
|
||||
description: "Patch one connector mapping.",
|
||||
method: "PATCH",
|
||||
path: pluginArchRoutePaths.connectorMapping,
|
||||
request: { body: connectorMappingUpdateSchema, params: connectorMappingParamsSchema },
|
||||
response: { description: "Connector mapping updated successfully.", schema: connectorMappingMutationResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
deleteConnectorMapping: {
|
||||
audience: "admin",
|
||||
description: "Delete one connector mapping.",
|
||||
method: "DELETE",
|
||||
path: pluginArchRoutePaths.connectorMapping,
|
||||
request: { params: connectorMappingParamsSchema },
|
||||
response: { description: "Connector mapping deleted successfully.", status: 204 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
listConnectorSyncEvents: {
|
||||
audience: "admin",
|
||||
description: "List connector sync events for inspection and debugging.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.connectorSyncEvents,
|
||||
request: { params: orgIdParamSchema, query: connectorSyncEventListQuerySchema },
|
||||
response: { description: "Connector sync event list.", schema: connectorSyncEventListResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
getConnectorSyncEvent: {
|
||||
audience: "admin",
|
||||
description: "Get one connector sync event.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.connectorSyncEvent,
|
||||
request: { params: connectorSyncEventParamsSchema },
|
||||
response: { description: "Connector sync event detail.", schema: connectorSyncEventDetailResponseSchema, status: 200 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
retryConnectorSyncEvent: {
|
||||
audience: "admin",
|
||||
description: "Queue a retry for a failed or partial connector sync event.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.connectorSyncEventRetry,
|
||||
request: { params: connectorSyncEventParamsSchema },
|
||||
response: { description: "Connector sync retry queued successfully.", schema: connectorSyncAsyncResponseSchema, status: 202 },
|
||||
tag: "Connectors",
|
||||
},
|
||||
githubSetup: {
|
||||
audience: "admin",
|
||||
description: "Create the GitHub connector account, instance, target, and initial mappings in one setup flow.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.githubSetup,
|
||||
request: { body: githubConnectorSetupSchema, params: orgIdParamSchema },
|
||||
response: { description: "GitHub connector setup created successfully.", schema: githubSetupResponseSchema, status: 201 },
|
||||
tag: "GitHub",
|
||||
},
|
||||
githubCreateAccount: {
|
||||
audience: "admin",
|
||||
description: "Persist a GitHub App installation as a reusable connector account.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.githubAccounts,
|
||||
request: { body: githubConnectorAccountCreateSchema, params: orgIdParamSchema },
|
||||
response: { description: "GitHub connector account created successfully.", schema: connectorAccountMutationResponseSchema, status: 201 },
|
||||
tag: "GitHub",
|
||||
},
|
||||
githubListRepositories: {
|
||||
audience: "admin",
|
||||
description: "List repositories visible to one GitHub connector account.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.githubAccountRepositories,
|
||||
request: { params: connectorAccountRepositoryParamsSchema, query: githubRepositoryListQuerySchema },
|
||||
response: { description: "GitHub repositories visible to the installation.", schema: githubRepositoryListResponseSchema, status: 200 },
|
||||
tag: "GitHub",
|
||||
},
|
||||
githubValidateTarget: {
|
||||
audience: "admin",
|
||||
description: "Validate one GitHub repository-branch target before persisting it.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.githubValidateTarget,
|
||||
request: { body: githubValidateTargetSchema, params: orgIdParamSchema },
|
||||
response: { description: "GitHub target validation result.", schema: githubValidateTargetResponseSchema, status: 200 },
|
||||
tag: "GitHub",
|
||||
},
|
||||
githubWebhookIngress: {
|
||||
audience: "public_webhook",
|
||||
description: "Accept a GitHub App webhook delivery, verify the raw-body signature, and enqueue any relevant sync work.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.githubWebhookIngress,
|
||||
request: { body: githubWebhookRawBodySchema, headers: githubWebhookHeadersSchema },
|
||||
response: { description: "Valid webhook accepted or ignored.", schema: githubWebhookAcceptedResponseSchema.or(githubWebhookIgnoredResponseSchema), status: 202 },
|
||||
tag: "Webhooks",
|
||||
},
|
||||
}
|
||||
|
||||
export const deferredPluginArchEndpointContracts: DeferredEndpointContract[] = [
|
||||
{
|
||||
description: "Compare two config object versions.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.configObjectCompareVersions,
|
||||
reason: "Diff semantics can wait until immutable version storage exists.",
|
||||
tag: "Config Objects",
|
||||
},
|
||||
{
|
||||
description: "Type-specific convenience endpoints for skills.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.skills,
|
||||
reason: "Shared config-object routes land first; per-type wrappers follow once the core surface is working.",
|
||||
tag: "Config Objects",
|
||||
},
|
||||
{
|
||||
description: "Type-specific convenience endpoints for agents.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.agents,
|
||||
reason: "Shared config-object routes land first; per-type wrappers follow once the core surface is working.",
|
||||
tag: "Config Objects",
|
||||
},
|
||||
{
|
||||
description: "Type-specific convenience endpoints for commands.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.commands,
|
||||
reason: "Shared config-object routes land first; per-type wrappers follow once the core surface is working.",
|
||||
tag: "Config Objects",
|
||||
},
|
||||
{
|
||||
description: "Type-specific convenience endpoints for tools.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.tools,
|
||||
reason: "Shared config-object routes land first; per-type wrappers follow once the core surface is working.",
|
||||
tag: "Config Objects",
|
||||
},
|
||||
{
|
||||
description: "Type-specific convenience endpoints for MCPs.",
|
||||
method: "GET",
|
||||
path: pluginArchRoutePaths.mcps,
|
||||
reason: "Shared config-object routes land first; per-type wrappers follow once the core surface is working.",
|
||||
tag: "Config Objects",
|
||||
},
|
||||
{
|
||||
description: "Create and list plugin releases.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.pluginReleases,
|
||||
reason: "Delivery and release snapshots stay deferred until the admin and webhook slice is live.",
|
||||
tag: "Plugins",
|
||||
},
|
||||
{
|
||||
description: "Preview one connector mapping against remote source data.",
|
||||
method: "POST",
|
||||
path: pluginArchRoutePaths.connectorMappingPreview,
|
||||
reason: "Mapping preview depends on the later reconciliation engine and should not block the first admin slice.",
|
||||
tag: "Connectors",
|
||||
},
|
||||
]
|
||||
|
||||
export const pluginArchContractSummary = {
|
||||
implementationHome: {
|
||||
adminApi: "ee/apps/den-api/src/routes/org",
|
||||
persistence: "ee/packages/den-db/src/schema",
|
||||
webhookIngress: "ee/apps/den-api/src/routes",
|
||||
},
|
||||
outOfScope: [
|
||||
"plugin delivery/install endpoints",
|
||||
"plugin release snapshot implementation",
|
||||
"type-specific convenience wrappers",
|
||||
],
|
||||
} as const
|
||||
5
ee/apps/den-api/src/routes/org/plugin-system/index.ts
Normal file
5
ee/apps/den-api/src/routes/org/plugin-system/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from "./contracts.js"
|
||||
export * from "./access.js"
|
||||
export * from "./routes.js"
|
||||
export * from "./schemas.js"
|
||||
export * from "./store.js"
|
||||
1455
ee/apps/den-api/src/routes/org/plugin-system/routes.ts
Normal file
1455
ee/apps/den-api/src/routes/org/plugin-system/routes.ts
Normal file
File diff suppressed because it is too large
Load Diff
653
ee/apps/den-api/src/routes/org/plugin-system/schemas.ts
Normal file
653
ee/apps/den-api/src/routes/org/plugin-system/schemas.ts
Normal file
@@ -0,0 +1,653 @@
|
||||
import {
|
||||
accessRoleValues,
|
||||
configObjectCreatedViaValues,
|
||||
configObjectSourceModeValues,
|
||||
configObjectStatusValues,
|
||||
configObjectTypeValues,
|
||||
connectorAccountStatusValues,
|
||||
connectorInstanceStatusValues,
|
||||
connectorMappingKindValues,
|
||||
connectorSyncEventTypeValues,
|
||||
connectorSyncStatusValues,
|
||||
connectorTargetKindValues,
|
||||
connectorTypeValues,
|
||||
membershipSourceValues,
|
||||
pluginStatusValues,
|
||||
} from "@openwork-ee/den-db/schema"
|
||||
import { z } from "zod"
|
||||
import { denTypeIdSchema } from "../../../openapi.js"
|
||||
import { idParamSchema, orgIdParamSchema } from "../shared.js"
|
||||
|
||||
const cursorSchema = z.string().trim().min(1).max(255)
|
||||
const jsonObjectSchema = z.object({}).passthrough()
|
||||
const rawSourceTextSchema = z.string().trim().min(1)
|
||||
const nullableStringSchema = z.string().trim().min(1).nullable()
|
||||
const nullableTimestampSchema = z.string().datetime({ offset: true }).nullable()
|
||||
const queryBooleanSchema = z.enum(["true", "false"]).transform((value) => value === "true")
|
||||
|
||||
export const githubWebhookEventValues = ["push", "installation", "installation_repositories", "repository"] as const
|
||||
|
||||
export const configObjectIdSchema = denTypeIdSchema("configObject")
|
||||
export const configObjectVersionIdSchema = denTypeIdSchema("configObjectVersion")
|
||||
export const configObjectAccessGrantIdSchema = denTypeIdSchema("configObjectAccessGrant")
|
||||
export const pluginIdSchema = denTypeIdSchema("plugin")
|
||||
export const pluginConfigObjectIdSchema = denTypeIdSchema("pluginConfigObject")
|
||||
export const pluginAccessGrantIdSchema = denTypeIdSchema("pluginAccessGrant")
|
||||
export const connectorAccountIdSchema = denTypeIdSchema("connectorAccount")
|
||||
export const connectorInstanceIdSchema = denTypeIdSchema("connectorInstance")
|
||||
export const connectorInstanceAccessGrantIdSchema = denTypeIdSchema("connectorInstanceAccessGrant")
|
||||
export const connectorTargetIdSchema = denTypeIdSchema("connectorTarget")
|
||||
export const connectorMappingIdSchema = denTypeIdSchema("connectorMapping")
|
||||
export const connectorSyncEventIdSchema = denTypeIdSchema("connectorSyncEvent")
|
||||
export const connectorSourceBindingIdSchema = denTypeIdSchema("connectorSourceBinding")
|
||||
export const connectorSourceTombstoneIdSchema = denTypeIdSchema("connectorSourceTombstone")
|
||||
export const memberIdSchema = denTypeIdSchema("member")
|
||||
export const teamIdSchema = denTypeIdSchema("team")
|
||||
|
||||
export const configObjectTypeSchema = z.enum(configObjectTypeValues)
|
||||
export const configObjectSourceModeSchema = z.enum(configObjectSourceModeValues)
|
||||
export const configObjectCreatedViaSchema = z.enum(configObjectCreatedViaValues)
|
||||
export const configObjectStatusSchema = z.enum(configObjectStatusValues)
|
||||
export const pluginStatusSchema = z.enum(pluginStatusValues)
|
||||
export const membershipSourceSchema = z.enum(membershipSourceValues)
|
||||
export const accessRoleSchema = z.enum(accessRoleValues)
|
||||
export const connectorTypeSchema = z.enum(connectorTypeValues)
|
||||
export const connectorAccountStatusSchema = z.enum(connectorAccountStatusValues)
|
||||
export const connectorInstanceStatusSchema = z.enum(connectorInstanceStatusValues)
|
||||
export const connectorTargetKindSchema = z.enum(connectorTargetKindValues)
|
||||
export const connectorMappingKindSchema = z.enum(connectorMappingKindValues)
|
||||
export const connectorSyncStatusSchema = z.enum(connectorSyncStatusValues)
|
||||
export const connectorSyncEventTypeSchema = z.enum(connectorSyncEventTypeValues)
|
||||
export const githubWebhookEventSchema = z.enum(githubWebhookEventValues)
|
||||
|
||||
export const pluginArchPaginationQuerySchema = z.object({
|
||||
cursor: cursorSchema.optional(),
|
||||
limit: z.coerce.number().int().min(1).max(100).optional(),
|
||||
})
|
||||
|
||||
export const configObjectListQuerySchema = pluginArchPaginationQuerySchema.extend({
|
||||
type: configObjectTypeSchema.optional(),
|
||||
status: configObjectStatusSchema.optional(),
|
||||
sourceMode: configObjectSourceModeSchema.optional(),
|
||||
pluginId: pluginIdSchema.optional(),
|
||||
connectorInstanceId: connectorInstanceIdSchema.optional(),
|
||||
includeDeleted: queryBooleanSchema.optional(),
|
||||
q: z.string().trim().min(1).max(255).optional(),
|
||||
})
|
||||
|
||||
export const configObjectVersionListQuerySchema = pluginArchPaginationQuerySchema.extend({
|
||||
includeDeleted: queryBooleanSchema.optional(),
|
||||
})
|
||||
|
||||
export const pluginListQuerySchema = pluginArchPaginationQuerySchema.extend({
|
||||
status: pluginStatusSchema.optional(),
|
||||
q: z.string().trim().min(1).max(255).optional(),
|
||||
})
|
||||
|
||||
export const connectorAccountListQuerySchema = pluginArchPaginationQuerySchema.extend({
|
||||
connectorType: connectorTypeSchema.optional(),
|
||||
status: connectorAccountStatusSchema.optional(),
|
||||
q: z.string().trim().min(1).max(255).optional(),
|
||||
})
|
||||
|
||||
export const connectorInstanceListQuerySchema = pluginArchPaginationQuerySchema.extend({
|
||||
connectorAccountId: connectorAccountIdSchema.optional(),
|
||||
connectorType: connectorTypeSchema.optional(),
|
||||
pluginId: pluginIdSchema.optional(),
|
||||
status: connectorInstanceStatusSchema.optional(),
|
||||
q: z.string().trim().min(1).max(255).optional(),
|
||||
})
|
||||
|
||||
export const connectorTargetListQuerySchema = pluginArchPaginationQuerySchema.extend({
|
||||
targetKind: connectorTargetKindSchema.optional(),
|
||||
q: z.string().trim().min(1).max(255).optional(),
|
||||
})
|
||||
|
||||
export const connectorMappingListQuerySchema = pluginArchPaginationQuerySchema.extend({
|
||||
mappingKind: connectorMappingKindSchema.optional(),
|
||||
objectType: configObjectTypeSchema.optional(),
|
||||
pluginId: pluginIdSchema.optional(),
|
||||
q: z.string().trim().min(1).max(255).optional(),
|
||||
})
|
||||
|
||||
export const connectorSyncEventListQuerySchema = pluginArchPaginationQuerySchema.extend({
|
||||
connectorInstanceId: connectorInstanceIdSchema.optional(),
|
||||
connectorTargetId: connectorTargetIdSchema.optional(),
|
||||
eventType: connectorSyncEventTypeSchema.optional(),
|
||||
status: connectorSyncStatusSchema.optional(),
|
||||
q: z.string().trim().min(1).max(255).optional(),
|
||||
})
|
||||
|
||||
export const githubRepositoryListQuerySchema = pluginArchPaginationQuerySchema.extend({
|
||||
q: z.string().trim().min(1).max(255).optional(),
|
||||
})
|
||||
|
||||
export const configObjectParamsSchema = orgIdParamSchema.extend(idParamSchema("configObjectId", "configObject").shape)
|
||||
export const configObjectVersionParamsSchema = configObjectParamsSchema.extend(idParamSchema("versionId", "configObjectVersion").shape)
|
||||
export const configObjectAccessGrantParamsSchema = configObjectParamsSchema.extend(idParamSchema("grantId", "configObjectAccessGrant").shape)
|
||||
export const pluginParamsSchema = orgIdParamSchema.extend(idParamSchema("pluginId", "plugin").shape)
|
||||
export const pluginConfigObjectParamsSchema = pluginParamsSchema.extend(idParamSchema("configObjectId", "configObject").shape)
|
||||
export const pluginAccessGrantParamsSchema = pluginParamsSchema.extend(idParamSchema("grantId", "pluginAccessGrant").shape)
|
||||
export const connectorAccountParamsSchema = orgIdParamSchema.extend(idParamSchema("connectorAccountId", "connectorAccount").shape)
|
||||
export const connectorInstanceParamsSchema = orgIdParamSchema.extend(idParamSchema("connectorInstanceId", "connectorInstance").shape)
|
||||
export const connectorInstanceAccessGrantParamsSchema = connectorInstanceParamsSchema.extend(idParamSchema("grantId", "connectorInstanceAccessGrant").shape)
|
||||
export const connectorTargetParamsSchema = orgIdParamSchema.extend(idParamSchema("connectorTargetId", "connectorTarget").shape)
|
||||
export const connectorMappingParamsSchema = orgIdParamSchema.extend(idParamSchema("connectorMappingId", "connectorMapping").shape)
|
||||
export const connectorSyncEventParamsSchema = orgIdParamSchema.extend(idParamSchema("connectorSyncEventId", "connectorSyncEvent").shape)
|
||||
|
||||
export const connectorAccountRepositoryParamsSchema = connectorAccountParamsSchema
|
||||
|
||||
export const configObjectInputSchema = z.object({
|
||||
rawSourceText: rawSourceTextSchema.optional(),
|
||||
normalizedPayloadJson: jsonObjectSchema.optional(),
|
||||
parserMode: z.string().trim().min(1).max(100).optional(),
|
||||
schemaVersion: z.string().trim().min(1).max(100).optional(),
|
||||
metadata: jsonObjectSchema.optional(),
|
||||
}).superRefine((value, ctx) => {
|
||||
if (!value.rawSourceText && !value.normalizedPayloadJson) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Provide either rawSourceText or normalizedPayloadJson.",
|
||||
path: ["rawSourceText"],
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export const configObjectCreateSchema = z.object({
|
||||
type: configObjectTypeSchema,
|
||||
sourceMode: configObjectSourceModeSchema,
|
||||
pluginIds: z.array(pluginIdSchema).max(100).optional(),
|
||||
input: configObjectInputSchema,
|
||||
})
|
||||
|
||||
export const configObjectCreateVersionSchema = z.object({
|
||||
input: configObjectInputSchema,
|
||||
reason: z.string().trim().min(1).max(255).optional(),
|
||||
})
|
||||
|
||||
export const configObjectPluginAttachSchema = z.object({
|
||||
pluginId: pluginIdSchema,
|
||||
membershipSource: membershipSourceSchema.optional(),
|
||||
})
|
||||
|
||||
export const resourceAccessGrantWriteSchema = z.object({
|
||||
orgMembershipId: memberIdSchema.optional(),
|
||||
teamId: teamIdSchema.optional(),
|
||||
orgWide: z.boolean().optional().default(false),
|
||||
role: accessRoleSchema,
|
||||
}).superRefine((value, ctx) => {
|
||||
const count = Number(Boolean(value.orgMembershipId)) + Number(Boolean(value.teamId)) + Number(Boolean(value.orgWide))
|
||||
if (count !== 1) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Provide exactly one of orgMembershipId, teamId, or orgWide=true.",
|
||||
path: ["orgMembershipId"],
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export const pluginCreateSchema = z.object({
|
||||
name: z.string().trim().min(1).max(255),
|
||||
description: nullableStringSchema.optional(),
|
||||
})
|
||||
|
||||
export const pluginUpdateSchema = z.object({
|
||||
name: z.string().trim().min(1).max(255).optional(),
|
||||
description: nullableStringSchema.optional(),
|
||||
}).superRefine((value, ctx) => {
|
||||
if (value.name === undefined && value.description === undefined) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Provide at least one field to update.",
|
||||
path: ["name"],
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export const pluginMembershipWriteSchema = z.object({
|
||||
configObjectId: configObjectIdSchema,
|
||||
membershipSource: membershipSourceSchema.optional(),
|
||||
})
|
||||
|
||||
export const connectorAccountCreateSchema = z.object({
|
||||
connectorType: connectorTypeSchema,
|
||||
remoteId: z.string().trim().min(1).max(255),
|
||||
externalAccountRef: z.string().trim().min(1).max(255).nullable().optional(),
|
||||
displayName: z.string().trim().min(1).max(255),
|
||||
metadata: jsonObjectSchema.optional(),
|
||||
})
|
||||
|
||||
export const connectorAccountDisconnectSchema = z.object({
|
||||
reason: z.string().trim().min(1).max(255).optional(),
|
||||
}).optional()
|
||||
|
||||
export const connectorInstanceCreateSchema = z.object({
|
||||
connectorAccountId: connectorAccountIdSchema,
|
||||
connectorType: connectorTypeSchema,
|
||||
remoteId: z.string().trim().min(1).max(255).nullable().optional(),
|
||||
name: z.string().trim().min(1).max(255),
|
||||
config: jsonObjectSchema.optional(),
|
||||
})
|
||||
|
||||
export const connectorInstanceUpdateSchema = z.object({
|
||||
remoteId: z.string().trim().min(1).max(255).nullable().optional(),
|
||||
name: z.string().trim().min(1).max(255).optional(),
|
||||
status: connectorInstanceStatusSchema.optional(),
|
||||
config: jsonObjectSchema.optional(),
|
||||
}).superRefine((value, ctx) => {
|
||||
if (value.remoteId === undefined && value.name === undefined && value.status === undefined && value.config === undefined) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Provide at least one field to update.",
|
||||
path: ["name"],
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export const connectorTargetCreateSchema = z.object({
|
||||
connectorType: connectorTypeSchema,
|
||||
remoteId: z.string().trim().min(1).max(255),
|
||||
targetKind: connectorTargetKindSchema,
|
||||
externalTargetRef: z.string().trim().min(1).max(255).nullable().optional(),
|
||||
config: jsonObjectSchema,
|
||||
})
|
||||
|
||||
export const connectorTargetUpdateSchema = z.object({
|
||||
remoteId: z.string().trim().min(1).max(255).optional(),
|
||||
externalTargetRef: z.string().trim().min(1).max(255).nullable().optional(),
|
||||
config: jsonObjectSchema.optional(),
|
||||
}).superRefine((value, ctx) => {
|
||||
if (value.remoteId === undefined && value.externalTargetRef === undefined && value.config === undefined) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Provide at least one field to update.",
|
||||
path: ["remoteId"],
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export const connectorMappingCreateSchema = z.object({
|
||||
mappingKind: connectorMappingKindSchema,
|
||||
selector: z.string().trim().min(1).max(255),
|
||||
objectType: configObjectTypeSchema,
|
||||
pluginId: pluginIdSchema.nullable().optional(),
|
||||
autoAddToPlugin: z.boolean().default(false),
|
||||
config: jsonObjectSchema.optional(),
|
||||
})
|
||||
|
||||
export const connectorMappingUpdateSchema = z.object({
|
||||
selector: z.string().trim().min(1).max(255).optional(),
|
||||
objectType: configObjectTypeSchema.optional(),
|
||||
pluginId: pluginIdSchema.nullable().optional(),
|
||||
autoAddToPlugin: z.boolean().optional(),
|
||||
config: jsonObjectSchema.optional(),
|
||||
}).superRefine((value, ctx) => {
|
||||
if (
|
||||
value.selector === undefined
|
||||
&& value.objectType === undefined
|
||||
&& value.pluginId === undefined
|
||||
&& value.autoAddToPlugin === undefined
|
||||
&& value.config === undefined
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Provide at least one field to update.",
|
||||
path: ["selector"],
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export const githubConnectorSetupSchema = z.object({
|
||||
installationId: z.number().int().positive(),
|
||||
connectorAccountId: connectorAccountIdSchema.optional(),
|
||||
connectorInstanceName: z.string().trim().min(1).max(255),
|
||||
repositoryId: z.number().int().positive(),
|
||||
repositoryFullName: z.string().trim().min(1).max(255),
|
||||
branch: z.string().trim().min(1).max(255),
|
||||
ref: z.string().trim().min(1).max(255),
|
||||
mappings: z.array(connectorMappingCreateSchema).max(100).default([]),
|
||||
})
|
||||
|
||||
export const githubConnectorAccountCreateSchema = z.object({
|
||||
installationId: z.number().int().positive(),
|
||||
accountLogin: z.string().trim().min(1).max(255),
|
||||
accountType: z.enum(["Organization", "User"]),
|
||||
displayName: z.string().trim().min(1).max(255),
|
||||
})
|
||||
|
||||
export const githubValidateTargetSchema = z.object({
|
||||
installationId: z.number().int().positive(),
|
||||
repositoryId: z.number().int().positive(),
|
||||
repositoryFullName: z.string().trim().min(1).max(255),
|
||||
branch: z.string().trim().min(1).max(255),
|
||||
ref: z.string().trim().min(1).max(255),
|
||||
})
|
||||
|
||||
export const accessGrantSchema = z.object({
|
||||
id: z.union([configObjectAccessGrantIdSchema, pluginAccessGrantIdSchema, connectorInstanceAccessGrantIdSchema]),
|
||||
orgMembershipId: memberIdSchema.nullable(),
|
||||
teamId: teamIdSchema.nullable(),
|
||||
orgWide: z.boolean(),
|
||||
role: accessRoleSchema,
|
||||
createdByOrgMembershipId: memberIdSchema,
|
||||
createdAt: z.string().datetime({ offset: true }),
|
||||
removedAt: nullableTimestampSchema,
|
||||
}).meta({ ref: "PluginArchAccessGrant" })
|
||||
|
||||
export const configObjectVersionSchema = z.object({
|
||||
id: configObjectVersionIdSchema,
|
||||
configObjectId: configObjectIdSchema,
|
||||
schemaVersion: z.string().trim().min(1).max(100).nullable(),
|
||||
normalizedPayloadJson: jsonObjectSchema.nullable(),
|
||||
rawSourceText: z.string().nullable(),
|
||||
createdVia: configObjectCreatedViaSchema,
|
||||
createdByOrgMembershipId: memberIdSchema.nullable(),
|
||||
connectorSyncEventId: connectorSyncEventIdSchema.nullable(),
|
||||
sourceRevisionRef: z.string().trim().min(1).max(255).nullable(),
|
||||
isDeletedVersion: z.boolean(),
|
||||
createdAt: z.string().datetime({ offset: true }),
|
||||
}).meta({ ref: "PluginArchConfigObjectVersion" })
|
||||
|
||||
export const configObjectSchema = z.object({
|
||||
id: configObjectIdSchema,
|
||||
organizationId: denTypeIdSchema("organization"),
|
||||
objectType: configObjectTypeSchema,
|
||||
sourceMode: configObjectSourceModeSchema,
|
||||
title: z.string().trim().min(1).max(255),
|
||||
description: nullableStringSchema,
|
||||
searchText: z.string().trim().min(1).max(65535).nullable(),
|
||||
currentFileName: z.string().trim().min(1).max(255).nullable(),
|
||||
currentFileExtension: z.string().trim().min(1).max(32).nullable(),
|
||||
currentRelativePath: z.string().trim().min(1).max(255).nullable(),
|
||||
status: configObjectStatusSchema,
|
||||
createdByOrgMembershipId: memberIdSchema,
|
||||
connectorInstanceId: connectorInstanceIdSchema.nullable(),
|
||||
createdAt: z.string().datetime({ offset: true }),
|
||||
updatedAt: z.string().datetime({ offset: true }),
|
||||
deletedAt: nullableTimestampSchema,
|
||||
latestVersion: configObjectVersionSchema.nullable(),
|
||||
}).meta({ ref: "PluginArchConfigObject" })
|
||||
|
||||
export const pluginMembershipSchema = z.object({
|
||||
id: pluginConfigObjectIdSchema,
|
||||
pluginId: pluginIdSchema,
|
||||
configObjectId: configObjectIdSchema,
|
||||
membershipSource: membershipSourceSchema,
|
||||
connectorMappingId: connectorMappingIdSchema.nullable(),
|
||||
createdByOrgMembershipId: memberIdSchema.nullable(),
|
||||
createdAt: z.string().datetime({ offset: true }),
|
||||
removedAt: nullableTimestampSchema,
|
||||
configObject: configObjectSchema.optional(),
|
||||
}).meta({ ref: "PluginArchPluginMembership" })
|
||||
|
||||
export const pluginSchema = z.object({
|
||||
id: pluginIdSchema,
|
||||
organizationId: denTypeIdSchema("organization"),
|
||||
name: z.string().trim().min(1).max(255),
|
||||
description: nullableStringSchema,
|
||||
status: pluginStatusSchema,
|
||||
createdByOrgMembershipId: memberIdSchema,
|
||||
createdAt: z.string().datetime({ offset: true }),
|
||||
updatedAt: z.string().datetime({ offset: true }),
|
||||
deletedAt: nullableTimestampSchema,
|
||||
memberCount: z.number().int().nonnegative().optional(),
|
||||
}).meta({ ref: "PluginArchPlugin" })
|
||||
|
||||
export const connectorAccountSchema = z.object({
|
||||
id: connectorAccountIdSchema,
|
||||
organizationId: denTypeIdSchema("organization"),
|
||||
connectorType: connectorTypeSchema,
|
||||
remoteId: z.string().trim().min(1).max(255),
|
||||
externalAccountRef: z.string().trim().min(1).max(255).nullable(),
|
||||
displayName: z.string().trim().min(1).max(255),
|
||||
status: connectorAccountStatusSchema,
|
||||
createdByOrgMembershipId: memberIdSchema,
|
||||
createdAt: z.string().datetime({ offset: true }),
|
||||
updatedAt: z.string().datetime({ offset: true }),
|
||||
metadata: jsonObjectSchema.optional(),
|
||||
}).meta({ ref: "PluginArchConnectorAccount" })
|
||||
|
||||
export const connectorInstanceSchema = z.object({
|
||||
id: connectorInstanceIdSchema,
|
||||
organizationId: denTypeIdSchema("organization"),
|
||||
connectorAccountId: connectorAccountIdSchema,
|
||||
connectorType: connectorTypeSchema,
|
||||
remoteId: z.string().trim().min(1).max(255).nullable(),
|
||||
name: z.string().trim().min(1).max(255),
|
||||
status: connectorInstanceStatusSchema,
|
||||
instanceConfigJson: jsonObjectSchema.nullable(),
|
||||
lastSyncedAt: nullableTimestampSchema,
|
||||
lastSyncStatus: connectorSyncStatusSchema.nullable(),
|
||||
lastSyncCursor: z.string().trim().min(1).max(255).nullable(),
|
||||
createdByOrgMembershipId: memberIdSchema,
|
||||
createdAt: z.string().datetime({ offset: true }),
|
||||
updatedAt: z.string().datetime({ offset: true }),
|
||||
}).meta({ ref: "PluginArchConnectorInstance" })
|
||||
|
||||
export const connectorTargetSchema = z.object({
|
||||
id: connectorTargetIdSchema,
|
||||
connectorInstanceId: connectorInstanceIdSchema,
|
||||
connectorType: connectorTypeSchema,
|
||||
remoteId: z.string().trim().min(1).max(255),
|
||||
targetKind: connectorTargetKindSchema,
|
||||
externalTargetRef: z.string().trim().min(1).max(255).nullable(),
|
||||
targetConfigJson: jsonObjectSchema,
|
||||
createdAt: z.string().datetime({ offset: true }),
|
||||
updatedAt: z.string().datetime({ offset: true }),
|
||||
}).meta({ ref: "PluginArchConnectorTarget" })
|
||||
|
||||
export const connectorMappingSchema = z.object({
|
||||
id: connectorMappingIdSchema,
|
||||
connectorInstanceId: connectorInstanceIdSchema,
|
||||
connectorTargetId: connectorTargetIdSchema,
|
||||
connectorType: connectorTypeSchema,
|
||||
remoteId: z.string().trim().min(1).max(255).nullable(),
|
||||
mappingKind: connectorMappingKindSchema,
|
||||
selector: z.string().trim().min(1).max(255),
|
||||
objectType: configObjectTypeSchema,
|
||||
pluginId: pluginIdSchema.nullable(),
|
||||
autoAddToPlugin: z.boolean(),
|
||||
mappingConfigJson: jsonObjectSchema.nullable(),
|
||||
createdAt: z.string().datetime({ offset: true }),
|
||||
updatedAt: z.string().datetime({ offset: true }),
|
||||
}).meta({ ref: "PluginArchConnectorMapping" })
|
||||
|
||||
export const connectorSyncSummarySchema = z.object({
|
||||
createdCount: z.number().int().nonnegative().optional(),
|
||||
updatedCount: z.number().int().nonnegative().optional(),
|
||||
deletedCount: z.number().int().nonnegative().optional(),
|
||||
skippedCount: z.number().int().nonnegative().optional(),
|
||||
failedCount: z.number().int().nonnegative().optional(),
|
||||
failures: z.array(jsonObjectSchema).optional(),
|
||||
}).passthrough().meta({ ref: "PluginArchConnectorSyncSummary" })
|
||||
|
||||
export const connectorSyncEventSchema = z.object({
|
||||
id: connectorSyncEventIdSchema,
|
||||
connectorInstanceId: connectorInstanceIdSchema,
|
||||
connectorTargetId: connectorTargetIdSchema.nullable(),
|
||||
connectorType: connectorTypeSchema,
|
||||
remoteId: z.string().trim().min(1).max(255).nullable(),
|
||||
eventType: connectorSyncEventTypeSchema,
|
||||
externalEventRef: z.string().trim().min(1).max(255).nullable(),
|
||||
sourceRevisionRef: z.string().trim().min(1).max(255).nullable(),
|
||||
status: connectorSyncStatusSchema,
|
||||
summaryJson: connectorSyncSummarySchema.nullable(),
|
||||
startedAt: z.string().datetime({ offset: true }),
|
||||
completedAt: nullableTimestampSchema,
|
||||
}).meta({ ref: "PluginArchConnectorSyncEvent" })
|
||||
|
||||
export const connectorSourceBindingSchema = z.object({
|
||||
id: connectorSourceBindingIdSchema,
|
||||
configObjectId: configObjectIdSchema,
|
||||
connectorInstanceId: connectorInstanceIdSchema,
|
||||
connectorTargetId: connectorTargetIdSchema,
|
||||
connectorMappingId: connectorMappingIdSchema,
|
||||
connectorType: connectorTypeSchema,
|
||||
remoteId: z.string().trim().min(1).max(255).nullable(),
|
||||
externalLocator: z.string().trim().min(1).max(255),
|
||||
externalStableRef: z.string().trim().min(1).max(255).nullable(),
|
||||
lastSeenSourceRevisionRef: z.string().trim().min(1).max(255).nullable(),
|
||||
status: configObjectStatusSchema,
|
||||
createdAt: z.string().datetime({ offset: true }),
|
||||
updatedAt: z.string().datetime({ offset: true }),
|
||||
deletedAt: nullableTimestampSchema,
|
||||
}).meta({ ref: "PluginArchConnectorSourceBinding" })
|
||||
|
||||
export const connectorSourceTombstoneSchema = z.object({
|
||||
id: connectorSourceTombstoneIdSchema,
|
||||
connectorInstanceId: connectorInstanceIdSchema,
|
||||
connectorTargetId: connectorTargetIdSchema,
|
||||
connectorMappingId: connectorMappingIdSchema,
|
||||
connectorType: connectorTypeSchema,
|
||||
remoteId: z.string().trim().min(1).max(255).nullable(),
|
||||
externalLocator: z.string().trim().min(1).max(255),
|
||||
formerConfigObjectId: configObjectIdSchema,
|
||||
deletedInSyncEventId: connectorSyncEventIdSchema,
|
||||
deletedSourceRevisionRef: z.string().trim().min(1).max(255).nullable(),
|
||||
createdAt: z.string().datetime({ offset: true }),
|
||||
}).meta({ ref: "PluginArchConnectorSourceTombstone" })
|
||||
|
||||
export const githubWebhookHeadersSchema = z.object({
|
||||
xHubSignature256: z.string().trim().min(1),
|
||||
xGithubEvent: githubWebhookEventSchema,
|
||||
xGithubDelivery: z.string().trim().min(1),
|
||||
}).meta({ ref: "PluginArchGithubWebhookHeaders" })
|
||||
|
||||
export const githubWebhookPayloadSchema = z.object({
|
||||
after: z.string().trim().min(1).optional(),
|
||||
installation: z.object({
|
||||
id: z.number().int().positive(),
|
||||
}).passthrough().optional(),
|
||||
ref: z.string().trim().min(1).optional(),
|
||||
repository: z.object({
|
||||
full_name: z.string().trim().min(1),
|
||||
id: z.number().int().positive(),
|
||||
}).passthrough().optional(),
|
||||
}).passthrough().meta({ ref: "PluginArchGithubWebhookPayload" })
|
||||
|
||||
export const githubWebhookEnvelopeSchema = z.object({
|
||||
deliveryId: z.string().trim().min(1),
|
||||
event: githubWebhookEventSchema,
|
||||
installationId: z.number().int().positive().optional(),
|
||||
repositoryId: z.number().int().positive().optional(),
|
||||
repositoryFullName: z.string().trim().min(1).optional(),
|
||||
ref: z.string().trim().min(1).optional(),
|
||||
headSha: z.string().trim().min(1).optional(),
|
||||
payload: githubWebhookPayloadSchema,
|
||||
}).meta({ ref: "PluginArchGithubWebhookEnvelope" })
|
||||
|
||||
export const githubConnectorSyncJobSchema = z.object({
|
||||
connectorType: z.literal("github"),
|
||||
connectorInstanceId: connectorInstanceIdSchema,
|
||||
connectorTargetId: connectorTargetIdSchema,
|
||||
connectorSyncEventId: connectorSyncEventIdSchema,
|
||||
deliveryId: z.string().trim().min(1),
|
||||
installationId: z.number().int().positive(),
|
||||
repositoryId: z.number().int().positive(),
|
||||
repositoryFullName: z.string().trim().min(1),
|
||||
ref: z.string().trim().min(1),
|
||||
headSha: z.string().trim().min(1),
|
||||
}).meta({ ref: "PluginArchGithubConnectorSyncJob" })
|
||||
|
||||
export const githubWebhookRawBodySchema = z.string().min(1).meta({ ref: "PluginArchGithubWebhookRawBody" })
|
||||
|
||||
export const githubWebhookAcceptedResponseSchema = z.object({
|
||||
ok: z.literal(true),
|
||||
accepted: z.literal(true),
|
||||
event: githubWebhookEventSchema,
|
||||
deliveryId: z.string().trim().min(1),
|
||||
queued: z.boolean(),
|
||||
}).meta({ ref: "PluginArchGithubWebhookAcceptedResponse" })
|
||||
|
||||
export const githubWebhookIgnoredResponseSchema = z.object({
|
||||
ok: z.literal(true),
|
||||
accepted: z.literal(false),
|
||||
reason: z.string().trim().min(1),
|
||||
}).meta({ ref: "PluginArchGithubWebhookIgnoredResponse" })
|
||||
|
||||
export const githubWebhookUnauthorizedResponseSchema = z.object({
|
||||
ok: z.literal(false),
|
||||
error: z.literal("invalid signature"),
|
||||
}).meta({ ref: "PluginArchGithubWebhookUnauthorizedResponse" })
|
||||
|
||||
export function pluginArchListResponseSchema<TSchema extends z.ZodTypeAny>(ref: string, itemSchema: TSchema) {
|
||||
return z.object({
|
||||
items: z.array(itemSchema),
|
||||
nextCursor: cursorSchema.nullable(),
|
||||
}).meta({ ref })
|
||||
}
|
||||
|
||||
export function pluginArchDetailResponseSchema<TSchema extends z.ZodTypeAny>(ref: string, itemSchema: TSchema) {
|
||||
return z.object({
|
||||
item: itemSchema,
|
||||
}).meta({ ref })
|
||||
}
|
||||
|
||||
export function pluginArchMutationResponseSchema<TSchema extends z.ZodTypeAny>(ref: string, itemSchema: TSchema) {
|
||||
return z.object({
|
||||
ok: z.literal(true),
|
||||
item: itemSchema,
|
||||
}).meta({ ref })
|
||||
}
|
||||
|
||||
export function pluginArchAsyncResponseSchema<TSchema extends z.ZodTypeAny>(ref: string, jobSchema: TSchema) {
|
||||
return z.object({
|
||||
ok: z.literal(true),
|
||||
queued: z.literal(true),
|
||||
job: jobSchema,
|
||||
}).meta({ ref })
|
||||
}
|
||||
|
||||
export const configObjectListResponseSchema = pluginArchListResponseSchema("PluginArchConfigObjectListResponse", configObjectSchema)
|
||||
export const configObjectDetailResponseSchema = pluginArchDetailResponseSchema("PluginArchConfigObjectDetailResponse", configObjectSchema)
|
||||
export const configObjectMutationResponseSchema = pluginArchMutationResponseSchema("PluginArchConfigObjectMutationResponse", configObjectSchema)
|
||||
export const configObjectVersionListResponseSchema = pluginArchListResponseSchema("PluginArchConfigObjectVersionListResponse", configObjectVersionSchema)
|
||||
export const configObjectVersionDetailResponseSchema = pluginArchDetailResponseSchema("PluginArchConfigObjectVersionDetailResponse", configObjectVersionSchema)
|
||||
export const pluginListResponseSchema = pluginArchListResponseSchema("PluginArchPluginListResponse", pluginSchema)
|
||||
export const pluginDetailResponseSchema = pluginArchDetailResponseSchema("PluginArchPluginDetailResponse", pluginSchema)
|
||||
export const pluginMutationResponseSchema = pluginArchMutationResponseSchema("PluginArchPluginMutationResponse", pluginSchema)
|
||||
export const pluginMembershipListResponseSchema = pluginArchListResponseSchema("PluginArchPluginMembershipListResponse", pluginMembershipSchema)
|
||||
export const pluginMembershipDetailResponseSchema = pluginArchDetailResponseSchema("PluginArchPluginMembershipDetailResponse", pluginMembershipSchema)
|
||||
export const pluginMembershipMutationResponseSchema = pluginArchMutationResponseSchema("PluginArchPluginMembershipMutationResponse", pluginMembershipSchema)
|
||||
export const accessGrantListResponseSchema = pluginArchListResponseSchema("PluginArchAccessGrantListResponse", accessGrantSchema)
|
||||
export const accessGrantMutationResponseSchema = pluginArchMutationResponseSchema("PluginArchAccessGrantMutationResponse", accessGrantSchema)
|
||||
export const connectorAccountListResponseSchema = pluginArchListResponseSchema("PluginArchConnectorAccountListResponse", connectorAccountSchema)
|
||||
export const connectorAccountDetailResponseSchema = pluginArchDetailResponseSchema("PluginArchConnectorAccountDetailResponse", connectorAccountSchema)
|
||||
export const connectorAccountMutationResponseSchema = pluginArchMutationResponseSchema("PluginArchConnectorAccountMutationResponse", connectorAccountSchema)
|
||||
export const connectorInstanceListResponseSchema = pluginArchListResponseSchema("PluginArchConnectorInstanceListResponse", connectorInstanceSchema)
|
||||
export const connectorInstanceDetailResponseSchema = pluginArchDetailResponseSchema("PluginArchConnectorInstanceDetailResponse", connectorInstanceSchema)
|
||||
export const connectorInstanceMutationResponseSchema = pluginArchMutationResponseSchema("PluginArchConnectorInstanceMutationResponse", connectorInstanceSchema)
|
||||
export const connectorTargetListResponseSchema = pluginArchListResponseSchema("PluginArchConnectorTargetListResponse", connectorTargetSchema)
|
||||
export const connectorTargetDetailResponseSchema = pluginArchDetailResponseSchema("PluginArchConnectorTargetDetailResponse", connectorTargetSchema)
|
||||
export const connectorTargetMutationResponseSchema = pluginArchMutationResponseSchema("PluginArchConnectorTargetMutationResponse", connectorTargetSchema)
|
||||
export const connectorMappingListResponseSchema = pluginArchListResponseSchema("PluginArchConnectorMappingListResponse", connectorMappingSchema)
|
||||
export const connectorMappingMutationResponseSchema = pluginArchMutationResponseSchema("PluginArchConnectorMappingMutationResponse", connectorMappingSchema)
|
||||
export const connectorSyncEventListResponseSchema = pluginArchListResponseSchema("PluginArchConnectorSyncEventListResponse", connectorSyncEventSchema)
|
||||
export const connectorSyncEventDetailResponseSchema = pluginArchDetailResponseSchema("PluginArchConnectorSyncEventDetailResponse", connectorSyncEventSchema)
|
||||
export const connectorSyncAsyncResponseSchema = pluginArchAsyncResponseSchema(
|
||||
"PluginArchConnectorSyncAsyncResponse",
|
||||
z.object({ id: connectorSyncEventIdSchema }),
|
||||
)
|
||||
export const githubRepositorySchema = z.object({
|
||||
id: z.number().int().positive(),
|
||||
fullName: z.string().trim().min(1),
|
||||
defaultBranch: z.string().trim().min(1).nullable(),
|
||||
private: z.boolean(),
|
||||
}).meta({ ref: "PluginArchGithubRepository" })
|
||||
export const githubRepositoryListResponseSchema = pluginArchListResponseSchema("PluginArchGithubRepositoryListResponse", githubRepositorySchema)
|
||||
export const githubSetupResponseSchema = pluginArchMutationResponseSchema(
|
||||
"PluginArchGithubSetupResponse",
|
||||
z.object({
|
||||
connectorAccount: connectorAccountSchema,
|
||||
connectorInstance: connectorInstanceSchema,
|
||||
connectorTarget: connectorTargetSchema,
|
||||
}),
|
||||
)
|
||||
export const githubValidateTargetResponseSchema = pluginArchMutationResponseSchema(
|
||||
"PluginArchGithubValidateTargetResponse",
|
||||
z.object({
|
||||
branchExists: z.boolean(),
|
||||
defaultBranch: z.string().trim().min(1).nullable(),
|
||||
repositoryAccessible: z.boolean(),
|
||||
}),
|
||||
)
|
||||
1675
ee/apps/den-api/src/routes/org/plugin-system/store.ts
Normal file
1675
ee/apps/den-api/src/routes/org/plugin-system/store.ts
Normal file
File diff suppressed because it is too large
Load Diff
100
ee/apps/den-api/src/routes/webhooks/github.ts
Normal file
100
ee/apps/den-api/src/routes/webhooks/github.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { createHmac, timingSafeEqual } from "node:crypto"
|
||||
import type { Env, Hono } from "hono"
|
||||
import { describeRoute } from "hono-openapi"
|
||||
import { env } from "../../env.js"
|
||||
import { emptyResponse, jsonResponse } from "../../openapi.js"
|
||||
import { enqueueGithubWebhookSync } from "../org/plugin-system/store.js"
|
||||
import {
|
||||
githubWebhookAcceptedResponseSchema,
|
||||
githubWebhookIgnoredResponseSchema,
|
||||
githubWebhookUnauthorizedResponseSchema,
|
||||
} from "../org/plugin-system/schemas.js"
|
||||
import { pluginArchRoutePaths } from "../org/plugin-system/contracts.js"
|
||||
|
||||
export function signGithubBody(rawBody: string, secret: string) {
|
||||
return `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`
|
||||
}
|
||||
|
||||
export function safeCompareGithubSignature(received: string, expected: string) {
|
||||
const encoder = new TextEncoder()
|
||||
const receivedBuffer = encoder.encode(received)
|
||||
const expectedBuffer = encoder.encode(expected)
|
||||
if (receivedBuffer.length !== expectedBuffer.length) {
|
||||
return false
|
||||
}
|
||||
return timingSafeEqual(receivedBuffer, expectedBuffer)
|
||||
}
|
||||
|
||||
export function registerGithubWebhookRoutes<T extends Env>(app: Hono<T>) {
|
||||
app.post(
|
||||
pluginArchRoutePaths.githubWebhookIngress,
|
||||
describeRoute({
|
||||
tags: ["Webhooks"],
|
||||
summary: "GitHub webhook ingress",
|
||||
description: "Verifies a GitHub App webhook signature against the raw request body, then records any relevant sync work.",
|
||||
responses: {
|
||||
200: jsonResponse("Ignored but valid GitHub webhook delivery.", githubWebhookIgnoredResponseSchema),
|
||||
202: jsonResponse("Accepted GitHub webhook delivery.", githubWebhookAcceptedResponseSchema),
|
||||
401: jsonResponse("Invalid GitHub webhook signature.", githubWebhookUnauthorizedResponseSchema),
|
||||
503: emptyResponse("GitHub webhook secret is not configured."),
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
const secret = env.githubConnectorApp.webhookSecret
|
||||
if (!secret) {
|
||||
return c.body(null, 503)
|
||||
}
|
||||
|
||||
const rawBody = await c.req.raw.text()
|
||||
const signature = c.req.raw.headers.get("x-hub-signature-256")?.trim() ?? ""
|
||||
if (!signature) {
|
||||
return c.json({ ok: false, error: "invalid signature" }, 401)
|
||||
}
|
||||
|
||||
const expected = signGithubBody(rawBody, secret)
|
||||
if (!safeCompareGithubSignature(signature, expected)) {
|
||||
return c.json({ ok: false, error: "invalid signature" }, 401)
|
||||
}
|
||||
|
||||
const event = c.req.raw.headers.get("x-github-event")?.trim() ?? ""
|
||||
const deliveryId = c.req.raw.headers.get("x-github-delivery")?.trim() ?? ""
|
||||
if (!event || !deliveryId) {
|
||||
return c.json({ ok: true, accepted: false, reason: "event ignored" }, 200)
|
||||
}
|
||||
|
||||
const normalizedEvent = event === "push" || event === "installation" || event === "installation_repositories" || event === "repository"
|
||||
? event
|
||||
: null
|
||||
if (!normalizedEvent) {
|
||||
return c.json({ ok: true, accepted: false, reason: "event ignored" }, 200)
|
||||
}
|
||||
|
||||
const payload = JSON.parse(rawBody) as Record<string, unknown>
|
||||
const installationId = payload.installation && typeof payload.installation === "object" && typeof (payload.installation as Record<string, unknown>).id === "number"
|
||||
? (payload.installation as Record<string, unknown>).id as number
|
||||
: undefined
|
||||
const repository = payload.repository && typeof payload.repository === "object" ? payload.repository as Record<string, unknown> : null
|
||||
const repositoryFullName = typeof repository?.full_name === "string" ? repository.full_name : undefined
|
||||
const repositoryId = typeof repository?.id === "number" ? repository.id : undefined
|
||||
const ref = typeof payload.ref === "string" ? payload.ref : undefined
|
||||
const headSha = typeof payload.after === "string" ? payload.after : undefined
|
||||
|
||||
const accepted = await enqueueGithubWebhookSync({
|
||||
deliveryId,
|
||||
event: normalizedEvent,
|
||||
headSha,
|
||||
installationId,
|
||||
payload,
|
||||
ref,
|
||||
repositoryFullName,
|
||||
repositoryId,
|
||||
})
|
||||
|
||||
if (!accepted.accepted) {
|
||||
return c.json({ ok: true, accepted: false, reason: accepted.reason }, 200)
|
||||
}
|
||||
|
||||
return c.json({ ok: true, accepted: true, deliveryId, event: normalizedEvent, queued: accepted.queued }, 202)
|
||||
},
|
||||
)
|
||||
}
|
||||
6
ee/apps/den-api/src/routes/webhooks/index.ts
Normal file
6
ee/apps/den-api/src/routes/webhooks/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { Env, Hono } from "hono"
|
||||
import { registerGithubWebhookRoutes } from "./github.js"
|
||||
|
||||
export function registerWebhookRoutes<T extends Env>(app: Hono<T>) {
|
||||
registerGithubWebhookRoutes(app)
|
||||
}
|
||||
91
ee/apps/den-api/test/github-webhook.test.ts
Normal file
91
ee/apps/den-api/test/github-webhook.test.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { afterEach, beforeAll, expect, test } from "bun:test"
|
||||
import { Hono } from "hono"
|
||||
|
||||
function seedRequiredEnv() {
|
||||
process.env.DATABASE_URL = process.env.DATABASE_URL ?? "mysql://root:password@127.0.0.1:3306/openwork_test"
|
||||
process.env.DEN_DB_ENCRYPTION_KEY = process.env.DEN_DB_ENCRYPTION_KEY ?? "x".repeat(32)
|
||||
process.env.BETTER_AUTH_SECRET = process.env.BETTER_AUTH_SECRET ?? "y".repeat(32)
|
||||
process.env.BETTER_AUTH_URL = process.env.BETTER_AUTH_URL ?? "http://127.0.0.1:8790"
|
||||
}
|
||||
|
||||
let envModule: typeof import("../src/env.js")
|
||||
let githubModule: typeof import("../src/routes/webhooks/github.js")
|
||||
|
||||
beforeAll(async () => {
|
||||
seedRequiredEnv()
|
||||
envModule = await import("../src/env.js")
|
||||
githubModule = await import("../src/routes/webhooks/github.js")
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
envModule.env.githubConnectorApp.webhookSecret = "super-secret"
|
||||
})
|
||||
|
||||
function createWebhookApp() {
|
||||
const app = new Hono()
|
||||
githubModule.registerGithubWebhookRoutes(app)
|
||||
return app
|
||||
}
|
||||
|
||||
test("webhook route rejects invalid signatures before JSON parsing", async () => {
|
||||
envModule.env.githubConnectorApp.webhookSecret = "super-secret"
|
||||
const app = createWebhookApp()
|
||||
const response = await app.request("http://den.local/api/webhooks/connectors/github", {
|
||||
body: "{",
|
||||
headers: {
|
||||
"x-github-delivery": "delivery-1",
|
||||
"x-github-event": "push",
|
||||
"x-hub-signature-256": "sha256=wrong",
|
||||
},
|
||||
method: "POST",
|
||||
})
|
||||
|
||||
expect(response.status).toBe(401)
|
||||
await expect(response.json()).resolves.toEqual({ ok: false, error: "invalid signature" })
|
||||
})
|
||||
|
||||
test("webhook route returns 503 when the GitHub webhook secret is unset", async () => {
|
||||
envModule.env.githubConnectorApp.webhookSecret = undefined
|
||||
const app = createWebhookApp()
|
||||
const response = await app.request("http://den.local/api/webhooks/connectors/github", {
|
||||
body: "{}",
|
||||
headers: {
|
||||
"x-github-delivery": "delivery-2",
|
||||
"x-github-event": "push",
|
||||
"x-hub-signature-256": "sha256=unused",
|
||||
},
|
||||
method: "POST",
|
||||
})
|
||||
|
||||
expect(response.status).toBe(503)
|
||||
})
|
||||
|
||||
test("webhook route accepts a valid signature and ignores unbound deliveries cleanly", async () => {
|
||||
envModule.env.githubConnectorApp.webhookSecret = "super-secret"
|
||||
const app = createWebhookApp()
|
||||
const payload = JSON.stringify({
|
||||
after: "abc123",
|
||||
ref: "refs/heads/main",
|
||||
repository: {
|
||||
full_name: "different-ai/openwork",
|
||||
id: 42,
|
||||
},
|
||||
})
|
||||
|
||||
const response = await app.request("http://den.local/api/webhooks/connectors/github", {
|
||||
body: payload,
|
||||
headers: {
|
||||
"x-github-delivery": "delivery-3",
|
||||
"x-github-event": "push",
|
||||
"x-hub-signature-256": githubModule.signGithubBody(payload, "super-secret"),
|
||||
},
|
||||
method: "POST",
|
||||
})
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
await expect(response.json()).resolves.toEqual({
|
||||
ok: true,
|
||||
accepted: false,
|
||||
reason: "missing installation id",
|
||||
})
|
||||
})
|
||||
90
ee/apps/den-api/test/plugin-system-access.test.ts
Normal file
90
ee/apps/den-api/test/plugin-system-access.test.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { beforeAll, expect, test } from "bun:test"
|
||||
|
||||
function seedRequiredEnv() {
|
||||
process.env.DATABASE_URL = process.env.DATABASE_URL ?? "mysql://root:password@127.0.0.1:3306/openwork_test"
|
||||
process.env.DEN_DB_ENCRYPTION_KEY = process.env.DEN_DB_ENCRYPTION_KEY ?? "x".repeat(32)
|
||||
process.env.BETTER_AUTH_SECRET = process.env.BETTER_AUTH_SECRET ?? "y".repeat(32)
|
||||
process.env.BETTER_AUTH_URL = process.env.BETTER_AUTH_URL ?? "http://127.0.0.1:8790"
|
||||
}
|
||||
|
||||
let accessModule: typeof import("../src/routes/org/plugin-system/access.js")
|
||||
|
||||
beforeAll(async () => {
|
||||
seedRequiredEnv()
|
||||
accessModule = await import("../src/routes/org/plugin-system/access.js")
|
||||
})
|
||||
|
||||
function createActorContext(input?: { isOwner?: boolean; role?: string; teamIds?: string[] }) {
|
||||
return {
|
||||
memberTeams: (input?.teamIds ?? []).map((teamId) => ({
|
||||
createdAt: new Date("2026-04-17T00:00:00.000Z"),
|
||||
id: teamId,
|
||||
name: teamId,
|
||||
organizationId: "org_test",
|
||||
updatedAt: new Date("2026-04-17T00:00:00.000Z"),
|
||||
})),
|
||||
organizationContext: {
|
||||
currentMember: {
|
||||
createdAt: new Date("2026-04-17T00:00:00.000Z"),
|
||||
id: "member_current",
|
||||
isOwner: input?.isOwner ?? false,
|
||||
role: input?.role ?? "member",
|
||||
userId: "user_current",
|
||||
},
|
||||
},
|
||||
} as any
|
||||
}
|
||||
|
||||
test("org owners and admins get plugin-system capability access", () => {
|
||||
expect(accessModule.isPluginArchOrgAdmin(createActorContext({ isOwner: true }))).toBe(true)
|
||||
expect(accessModule.isPluginArchOrgAdmin(createActorContext({ role: "member,admin" }))).toBe(true)
|
||||
expect(accessModule.isPluginArchOrgAdmin(createActorContext({ role: "member" }))).toBe(false)
|
||||
|
||||
expect(accessModule.hasPluginArchCapability(createActorContext({ isOwner: true }), "plugin.create")).toBe(true)
|
||||
expect(accessModule.hasPluginArchCapability(createActorContext({ role: "admin" }), "connector_instance.create")).toBe(true)
|
||||
expect(accessModule.hasPluginArchCapability(createActorContext({ role: "member" }), "config_object.create")).toBe(false)
|
||||
})
|
||||
|
||||
test("grant resolution supports direct, team, org-wide, and highest-role precedence", () => {
|
||||
const grants = [
|
||||
{
|
||||
orgMembershipId: null,
|
||||
orgWide: true,
|
||||
removedAt: null,
|
||||
role: "viewer",
|
||||
teamId: null,
|
||||
},
|
||||
{
|
||||
orgMembershipId: null,
|
||||
orgWide: false,
|
||||
removedAt: null,
|
||||
role: "editor",
|
||||
teamId: "team_alpha",
|
||||
},
|
||||
{
|
||||
orgMembershipId: "member_current",
|
||||
orgWide: false,
|
||||
removedAt: null,
|
||||
role: "manager",
|
||||
teamId: null,
|
||||
},
|
||||
] as const
|
||||
|
||||
expect(accessModule.resolvePluginArchGrantRole({ grants: [...grants], memberId: "member_current", teamIds: ["team_alpha"] })).toBe("manager")
|
||||
expect(accessModule.resolvePluginArchGrantRole({ grants: [...grants], memberId: "other_member", teamIds: ["team_alpha"] })).toBe("editor")
|
||||
expect(accessModule.resolvePluginArchGrantRole({ grants: [...grants], memberId: "other_member", teamIds: [] })).toBe("viewer")
|
||||
})
|
||||
|
||||
test("removed grants are ignored during resolution", () => {
|
||||
expect(accessModule.resolvePluginArchGrantRole({
|
||||
grants: [{
|
||||
orgMembershipId: "member_current",
|
||||
orgWide: false,
|
||||
removedAt: new Date("2026-04-17T00:00:00.000Z"),
|
||||
role: "manager",
|
||||
teamId: null,
|
||||
}],
|
||||
memberId: "member_current",
|
||||
teamIds: [],
|
||||
})).toBeNull()
|
||||
})
|
||||
284
ee/packages/den-db/drizzle/0010_plugin_arch.sql
Normal file
284
ee/packages/den-db/drizzle/0010_plugin_arch.sql
Normal file
@@ -0,0 +1,284 @@
|
||||
CREATE TABLE IF NOT EXISTS `config_object` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`organization_id` varchar(64) NOT NULL,
|
||||
`object_type` enum('skill','agent','command','tool','mcp','hook','context','custom') NOT NULL,
|
||||
`source_mode` enum('cloud','import','connector') NOT NULL,
|
||||
`title` varchar(255) NOT NULL,
|
||||
`description` text,
|
||||
`search_text` text,
|
||||
`current_file_name` varchar(255),
|
||||
`current_file_extension` varchar(64),
|
||||
`current_relative_path` varchar(2048),
|
||||
`status` enum('active','inactive','deleted','archived','ingestion_error') NOT NULL DEFAULT 'active',
|
||||
`created_by_org_membership_id` varchar(64) NOT NULL,
|
||||
`connector_instance_id` varchar(64),
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`deleted_at` timestamp(3) NULL,
|
||||
CONSTRAINT `config_object_id` PRIMARY KEY(`id`),
|
||||
KEY `config_object_organization_id` (`organization_id`),
|
||||
KEY `config_object_type` (`object_type`),
|
||||
KEY `config_object_source_mode` (`source_mode`),
|
||||
KEY `config_object_status` (`status`),
|
||||
KEY `config_object_created_by_org_membership_id` (`created_by_org_membership_id`),
|
||||
KEY `config_object_connector_instance_id` (`connector_instance_id`),
|
||||
KEY `config_object_current_relative_path` (`current_relative_path`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `config_object_version` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`config_object_id` varchar(64) NOT NULL,
|
||||
`normalized_payload_json` text,
|
||||
`raw_source_text` text,
|
||||
`schema_version` varchar(100),
|
||||
`created_via` enum('cloud','import','connector','system') NOT NULL,
|
||||
`created_by_org_membership_id` varchar(64),
|
||||
`connector_sync_event_id` varchar(64),
|
||||
`source_revision_ref` varchar(255),
|
||||
`is_deleted_version` boolean NOT NULL DEFAULT false,
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
CONSTRAINT `config_object_version_id` PRIMARY KEY(`id`),
|
||||
KEY `config_object_version_config_object_id` (`config_object_id`),
|
||||
KEY `config_object_version_created_by_org_membership_id` (`created_by_org_membership_id`),
|
||||
KEY `config_object_version_connector_sync_event_id` (`connector_sync_event_id`),
|
||||
KEY `config_object_version_source_revision_ref` (`source_revision_ref`),
|
||||
KEY `config_object_version_lookup_latest` (`config_object_id`, `created_at`, `id`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `plugin` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`organization_id` varchar(64) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`description` text,
|
||||
`status` enum('active','inactive','deleted','archived') NOT NULL DEFAULT 'active',
|
||||
`created_by_org_membership_id` varchar(64) NOT NULL,
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`deleted_at` timestamp(3) NULL,
|
||||
CONSTRAINT `plugin_id` PRIMARY KEY(`id`),
|
||||
KEY `plugin_organization_id` (`organization_id`),
|
||||
KEY `plugin_created_by_org_membership_id` (`created_by_org_membership_id`),
|
||||
KEY `plugin_status` (`status`),
|
||||
KEY `plugin_name` (`name`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `plugin_config_object` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`plugin_id` varchar(64) NOT NULL,
|
||||
`config_object_id` varchar(64) NOT NULL,
|
||||
`membership_source` enum('manual','connector','api','system') NOT NULL DEFAULT 'manual',
|
||||
`connector_mapping_id` varchar(64),
|
||||
`created_by_org_membership_id` varchar(64),
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`removed_at` timestamp(3) NULL,
|
||||
CONSTRAINT `plugin_config_object_id` PRIMARY KEY(`id`),
|
||||
CONSTRAINT `plugin_config_object_plugin_config_object` UNIQUE(`plugin_id`, `config_object_id`),
|
||||
KEY `plugin_config_object_plugin_id` (`plugin_id`),
|
||||
KEY `plugin_config_object_config_object_id` (`config_object_id`),
|
||||
KEY `plugin_config_object_connector_mapping_id` (`connector_mapping_id`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `config_object_access_grant` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`config_object_id` varchar(64) NOT NULL,
|
||||
`org_membership_id` varchar(64),
|
||||
`team_id` varchar(64),
|
||||
`org_wide` boolean NOT NULL DEFAULT false,
|
||||
`role` enum('viewer','editor','manager') NOT NULL,
|
||||
`created_by_org_membership_id` varchar(64) NOT NULL,
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`removed_at` timestamp(3) NULL,
|
||||
CONSTRAINT `config_object_access_grant_id` PRIMARY KEY(`id`),
|
||||
CONSTRAINT `config_object_access_grant_object_org_membership` UNIQUE(`config_object_id`, `org_membership_id`),
|
||||
CONSTRAINT `config_object_access_grant_object_team` UNIQUE(`config_object_id`, `team_id`),
|
||||
KEY `config_object_access_grant_config_object_id` (`config_object_id`),
|
||||
KEY `config_object_access_grant_org_membership_id` (`org_membership_id`),
|
||||
KEY `config_object_access_grant_team_id` (`team_id`),
|
||||
KEY `config_object_access_grant_org_wide` (`org_wide`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `plugin_access_grant` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`plugin_id` varchar(64) NOT NULL,
|
||||
`org_membership_id` varchar(64),
|
||||
`team_id` varchar(64),
|
||||
`org_wide` boolean NOT NULL DEFAULT false,
|
||||
`role` enum('viewer','editor','manager') NOT NULL,
|
||||
`created_by_org_membership_id` varchar(64) NOT NULL,
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`removed_at` timestamp(3) NULL,
|
||||
CONSTRAINT `plugin_access_grant_id` PRIMARY KEY(`id`),
|
||||
CONSTRAINT `plugin_access_grant_plugin_org_membership` UNIQUE(`plugin_id`, `org_membership_id`),
|
||||
CONSTRAINT `plugin_access_grant_plugin_team` UNIQUE(`plugin_id`, `team_id`),
|
||||
KEY `plugin_access_grant_plugin_id` (`plugin_id`),
|
||||
KEY `plugin_access_grant_org_membership_id` (`org_membership_id`),
|
||||
KEY `plugin_access_grant_team_id` (`team_id`),
|
||||
KEY `plugin_access_grant_org_wide` (`org_wide`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `connector_account` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`organization_id` varchar(64) NOT NULL,
|
||||
`connector_type` enum('github') NOT NULL,
|
||||
`remote_id` varchar(255) NOT NULL,
|
||||
`external_account_ref` varchar(255),
|
||||
`display_name` varchar(255) NOT NULL,
|
||||
`status` enum('active','inactive','disconnected','error') NOT NULL DEFAULT 'active',
|
||||
`created_by_org_membership_id` varchar(64) NOT NULL,
|
||||
`metadata_json` json,
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
CONSTRAINT `connector_account_id` PRIMARY KEY(`id`),
|
||||
CONSTRAINT `connector_account_org_type_remote_id` UNIQUE(`organization_id`, `connector_type`, `remote_id`),
|
||||
KEY `connector_account_organization_id` (`organization_id`),
|
||||
KEY `connector_account_created_by_org_membership_id` (`created_by_org_membership_id`),
|
||||
KEY `connector_account_connector_type` (`connector_type`),
|
||||
KEY `connector_account_status` (`status`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `connector_instance` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`organization_id` varchar(64) NOT NULL,
|
||||
`connector_account_id` varchar(64) NOT NULL,
|
||||
`connector_type` enum('github') NOT NULL,
|
||||
`remote_id` varchar(255),
|
||||
`name` varchar(255) NOT NULL,
|
||||
`status` enum('active','disabled','archived','error') NOT NULL DEFAULT 'active',
|
||||
`instance_config_json` json,
|
||||
`last_synced_at` timestamp(3) NULL,
|
||||
`last_sync_status` enum('pending','queued','running','completed','failed','partial','ignored'),
|
||||
`last_sync_cursor` text,
|
||||
`created_by_org_membership_id` varchar(64) NOT NULL,
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
CONSTRAINT `connector_instance_id` PRIMARY KEY(`id`),
|
||||
CONSTRAINT `connector_instance_org_name` UNIQUE(`organization_id`, `name`),
|
||||
KEY `connector_instance_organization_id` (`organization_id`),
|
||||
KEY `connector_instance_connector_account_id` (`connector_account_id`),
|
||||
KEY `connector_instance_created_by_org_membership_id` (`created_by_org_membership_id`),
|
||||
KEY `connector_instance_connector_type` (`connector_type`),
|
||||
KEY `connector_instance_status` (`status`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `connector_instance_access_grant` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`connector_instance_id` varchar(64) NOT NULL,
|
||||
`org_membership_id` varchar(64),
|
||||
`team_id` varchar(64),
|
||||
`org_wide` boolean NOT NULL DEFAULT false,
|
||||
`role` enum('viewer','editor','manager') NOT NULL,
|
||||
`created_by_org_membership_id` varchar(64) NOT NULL,
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`removed_at` timestamp(3) NULL,
|
||||
CONSTRAINT `connector_instance_access_grant_id` PRIMARY KEY(`id`),
|
||||
CONSTRAINT `connector_instance_access_grant_instance_org_membership` UNIQUE(`connector_instance_id`, `org_membership_id`),
|
||||
CONSTRAINT `connector_instance_access_grant_instance_team` UNIQUE(`connector_instance_id`, `team_id`),
|
||||
KEY `connector_instance_access_grant_instance_id` (`connector_instance_id`),
|
||||
KEY `connector_instance_access_grant_org_membership_id` (`org_membership_id`),
|
||||
KEY `connector_instance_access_grant_team_id` (`team_id`),
|
||||
KEY `connector_instance_access_grant_org_wide` (`org_wide`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `connector_target` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`connector_instance_id` varchar(64) NOT NULL,
|
||||
`connector_type` enum('github') NOT NULL,
|
||||
`remote_id` varchar(255) NOT NULL,
|
||||
`target_kind` enum('repository_branch') NOT NULL,
|
||||
`external_target_ref` varchar(255),
|
||||
`target_config_json` json NOT NULL,
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
CONSTRAINT `connector_target_id` PRIMARY KEY(`id`),
|
||||
CONSTRAINT `connector_target_instance_remote_id` UNIQUE(`connector_instance_id`, `remote_id`),
|
||||
KEY `connector_target_connector_instance_id` (`connector_instance_id`),
|
||||
KEY `connector_target_connector_type` (`connector_type`),
|
||||
KEY `connector_target_target_kind` (`target_kind`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `connector_mapping` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`connector_instance_id` varchar(64) NOT NULL,
|
||||
`connector_target_id` varchar(64) NOT NULL,
|
||||
`connector_type` enum('github') NOT NULL,
|
||||
`remote_id` varchar(255),
|
||||
`mapping_kind` enum('path','api','custom') NOT NULL,
|
||||
`selector` varchar(1024) NOT NULL,
|
||||
`object_type` enum('skill','agent','command','tool','mcp','hook','context','custom') NOT NULL,
|
||||
`plugin_id` varchar(64),
|
||||
`auto_add_to_plugin` boolean NOT NULL DEFAULT false,
|
||||
`mapping_config_json` json,
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
CONSTRAINT `connector_mapping_id` PRIMARY KEY(`id`),
|
||||
CONSTRAINT `connector_mapping_target_selector_object_type` UNIQUE(`connector_target_id`, `selector`, `object_type`),
|
||||
KEY `connector_mapping_connector_instance_id` (`connector_instance_id`),
|
||||
KEY `connector_mapping_connector_target_id` (`connector_target_id`),
|
||||
KEY `connector_mapping_object_type` (`object_type`),
|
||||
KEY `connector_mapping_plugin_id` (`plugin_id`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `connector_sync_event` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`connector_instance_id` varchar(64) NOT NULL,
|
||||
`connector_target_id` varchar(64),
|
||||
`connector_type` enum('github') NOT NULL,
|
||||
`remote_id` varchar(255),
|
||||
`event_type` enum('push','installation','installation_repositories','repository','manual_resync') NOT NULL,
|
||||
`external_event_ref` varchar(255),
|
||||
`source_revision_ref` varchar(255),
|
||||
`status` enum('pending','queued','running','completed','failed','partial','ignored') NOT NULL DEFAULT 'pending',
|
||||
`summary_json` json,
|
||||
`started_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`completed_at` timestamp(3) NULL,
|
||||
CONSTRAINT `connector_sync_event_id` PRIMARY KEY(`id`),
|
||||
KEY `connector_sync_event_connector_instance_id` (`connector_instance_id`),
|
||||
KEY `connector_sync_event_connector_target_id` (`connector_target_id`),
|
||||
KEY `connector_sync_event_event_type` (`event_type`),
|
||||
KEY `connector_sync_event_status` (`status`),
|
||||
KEY `connector_sync_event_source_revision_ref` (`source_revision_ref`),
|
||||
KEY `connector_sync_event_external_event_ref` (`external_event_ref`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `connector_source_binding` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`config_object_id` varchar(64) NOT NULL,
|
||||
`connector_instance_id` varchar(64) NOT NULL,
|
||||
`connector_target_id` varchar(64) NOT NULL,
|
||||
`connector_mapping_id` varchar(64) NOT NULL,
|
||||
`connector_type` enum('github') NOT NULL,
|
||||
`remote_id` varchar(255),
|
||||
`external_locator` varchar(2048) NOT NULL,
|
||||
`external_stable_ref` varchar(255),
|
||||
`last_seen_source_revision_ref` varchar(255),
|
||||
`status` enum('active','inactive','deleted','archived','ingestion_error') NOT NULL DEFAULT 'active',
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`deleted_at` timestamp(3) NULL,
|
||||
CONSTRAINT `connector_source_binding_id` PRIMARY KEY(`id`),
|
||||
CONSTRAINT `connector_source_binding_config_object` UNIQUE(`config_object_id`),
|
||||
KEY `connector_source_binding_connector_instance_id` (`connector_instance_id`),
|
||||
KEY `connector_source_binding_connector_target_id` (`connector_target_id`),
|
||||
KEY `connector_source_binding_connector_mapping_id` (`connector_mapping_id`),
|
||||
KEY `connector_source_binding_external_locator` (`external_locator`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `connector_source_tombstone` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`connector_instance_id` varchar(64) NOT NULL,
|
||||
`connector_target_id` varchar(64) NOT NULL,
|
||||
`connector_mapping_id` varchar(64) NOT NULL,
|
||||
`connector_type` enum('github') NOT NULL,
|
||||
`remote_id` varchar(255),
|
||||
`external_locator` varchar(2048) NOT NULL,
|
||||
`former_config_object_id` varchar(64) NOT NULL,
|
||||
`deleted_in_sync_event_id` varchar(64) NOT NULL,
|
||||
`deleted_source_revision_ref` varchar(255),
|
||||
`created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
CONSTRAINT `connector_source_tombstone_id` PRIMARY KEY(`id`),
|
||||
KEY `connector_source_tombstone_connector_instance_id` (`connector_instance_id`),
|
||||
KEY `connector_source_tombstone_connector_target_id` (`connector_target_id`),
|
||||
KEY `connector_source_tombstone_connector_mapping_id` (`connector_mapping_id`),
|
||||
KEY `connector_source_tombstone_external_locator` (`external_locator`),
|
||||
KEY `connector_source_tombstone_former_config_object_id` (`former_config_object_id`)
|
||||
);
|
||||
@@ -64,6 +64,13 @@
|
||||
"when": 1775350000000,
|
||||
"tag": "0009_api_keys",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 10,
|
||||
"version": "5",
|
||||
"when": 1776427000000,
|
||||
"tag": "0010_plugin_arch",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from "./auth"
|
||||
export * from "./org"
|
||||
export * from "./sharables/llm-providers"
|
||||
export * from "./sharables/plugin-arch"
|
||||
export * from "./sharables/skills"
|
||||
export * from "./teams"
|
||||
export * from "./workers"
|
||||
|
||||
656
ee/packages/den-db/src/schema/sharables/plugin-arch.ts
Normal file
656
ee/packages/den-db/src/schema/sharables/plugin-arch.ts
Normal file
@@ -0,0 +1,656 @@
|
||||
import { relations, sql } from "drizzle-orm"
|
||||
import {
|
||||
boolean,
|
||||
index,
|
||||
json,
|
||||
mysqlEnum,
|
||||
mysqlTable,
|
||||
text,
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
varchar,
|
||||
} from "drizzle-orm/mysql-core"
|
||||
import { denTypeIdColumn, encryptedColumn, encryptedTextColumn } from "../../columns"
|
||||
import { MemberTable, OrganizationTable } from "../org"
|
||||
import { TeamTable } from "../teams"
|
||||
|
||||
export const configObjectTypeValues = ["skill", "agent", "command", "tool", "mcp", "hook", "context", "custom"] as const
|
||||
export const configObjectSourceModeValues = ["cloud", "import", "connector"] as const
|
||||
export const configObjectStatusValues = ["active", "inactive", "deleted", "archived", "ingestion_error"] as const
|
||||
export const configObjectCreatedViaValues = ["cloud", "import", "connector", "system"] as const
|
||||
export const pluginStatusValues = ["active", "inactive", "deleted", "archived"] as const
|
||||
export const membershipSourceValues = ["manual", "connector", "api", "system"] as const
|
||||
export const accessRoleValues = ["viewer", "editor", "manager"] as const
|
||||
export const connectorTypeValues = ["github"] as const
|
||||
export const connectorAccountStatusValues = ["active", "inactive", "disconnected", "error"] as const
|
||||
export const connectorInstanceStatusValues = ["active", "disabled", "archived", "error"] as const
|
||||
export const connectorTargetKindValues = ["repository_branch"] as const
|
||||
export const connectorMappingKindValues = ["path", "api", "custom"] as const
|
||||
export const connectorSyncEventTypeValues = ["push", "installation", "installation_repositories", "repository", "manual_resync"] as const
|
||||
export const connectorSyncStatusValues = ["pending", "queued", "running", "completed", "failed", "partial", "ignored"] as const
|
||||
|
||||
function encryptedJsonColumn<TData extends Record<string, unknown> | Array<unknown> | null>(columnName: string) {
|
||||
return encryptedColumn<TData>(columnName, {
|
||||
deserialize: (value) => JSON.parse(value) as TData,
|
||||
serialize: (value) => JSON.stringify(value),
|
||||
})
|
||||
}
|
||||
|
||||
export const ConfigObjectTable = mysqlTable(
|
||||
"config_object",
|
||||
{
|
||||
id: denTypeIdColumn("configObject", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
objectType: mysqlEnum("object_type", configObjectTypeValues).notNull(),
|
||||
sourceMode: mysqlEnum("source_mode", configObjectSourceModeValues).notNull(),
|
||||
title: varchar("title", { length: 255 }).notNull(),
|
||||
description: text("description"),
|
||||
searchText: text("search_text"),
|
||||
currentFileName: varchar("current_file_name", { length: 255 }),
|
||||
currentFileExtension: varchar("current_file_extension", { length: 64 }),
|
||||
currentRelativePath: varchar("current_relative_path", { length: 255 }),
|
||||
status: mysqlEnum("status", configObjectStatusValues).notNull().default("active"),
|
||||
createdByOrgMembershipId: denTypeIdColumn("member", "created_by_org_membership_id").notNull(),
|
||||
connectorInstanceId: denTypeIdColumn("connectorInstance", "connector_instance_id"),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { fsp: 3 }).notNull().default(sql`CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)`),
|
||||
deletedAt: timestamp("deleted_at", { fsp: 3 }),
|
||||
},
|
||||
(table) => [
|
||||
index("config_object_organization_id").on(table.organizationId),
|
||||
index("config_object_type").on(table.objectType),
|
||||
index("config_object_source_mode").on(table.sourceMode),
|
||||
index("config_object_status").on(table.status),
|
||||
index("config_object_created_by_org_membership_id").on(table.createdByOrgMembershipId),
|
||||
index("config_object_connector_instance_id").on(table.connectorInstanceId),
|
||||
index("config_object_current_relative_path").on(table.currentRelativePath),
|
||||
],
|
||||
)
|
||||
|
||||
export const ConfigObjectVersionTable = mysqlTable(
|
||||
"config_object_version",
|
||||
{
|
||||
id: denTypeIdColumn("configObjectVersion", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
configObjectId: denTypeIdColumn("configObject", "config_object_id").notNull(),
|
||||
normalizedPayloadJson: encryptedJsonColumn<Record<string, unknown> | null>("normalized_payload_json"),
|
||||
rawSourceText: encryptedTextColumn("raw_source_text"),
|
||||
schemaVersion: varchar("schema_version", { length: 100 }),
|
||||
createdVia: mysqlEnum("created_via", configObjectCreatedViaValues).notNull(),
|
||||
createdByOrgMembershipId: denTypeIdColumn("member", "created_by_org_membership_id"),
|
||||
connectorSyncEventId: denTypeIdColumn("connectorSyncEvent", "connector_sync_event_id"),
|
||||
sourceRevisionRef: varchar("source_revision_ref", { length: 255 }),
|
||||
isDeletedVersion: boolean("is_deleted_version").notNull().default(false),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
},
|
||||
(table) => [
|
||||
index("config_object_version_organization_id").on(table.organizationId),
|
||||
index("config_object_version_config_object_id").on(table.configObjectId),
|
||||
index("config_object_version_created_by_org_membership_id").on(table.createdByOrgMembershipId),
|
||||
index("config_object_version_connector_sync_event_id").on(table.connectorSyncEventId),
|
||||
index("config_object_version_source_revision_ref").on(table.sourceRevisionRef),
|
||||
index("config_object_version_lookup_latest").on(table.configObjectId, table.createdAt, table.id),
|
||||
],
|
||||
)
|
||||
|
||||
export const PluginTable = mysqlTable(
|
||||
"plugin",
|
||||
{
|
||||
id: denTypeIdColumn("plugin", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
description: text("description"),
|
||||
status: mysqlEnum("status", pluginStatusValues).notNull().default("active"),
|
||||
createdByOrgMembershipId: denTypeIdColumn("member", "created_by_org_membership_id").notNull(),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { fsp: 3 }).notNull().default(sql`CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)`),
|
||||
deletedAt: timestamp("deleted_at", { fsp: 3 }),
|
||||
},
|
||||
(table) => [
|
||||
index("plugin_organization_id").on(table.organizationId),
|
||||
index("plugin_created_by_org_membership_id").on(table.createdByOrgMembershipId),
|
||||
index("plugin_status").on(table.status),
|
||||
index("plugin_name").on(table.name),
|
||||
],
|
||||
)
|
||||
|
||||
export const PluginConfigObjectTable = mysqlTable(
|
||||
"plugin_config_object",
|
||||
{
|
||||
id: denTypeIdColumn("pluginConfigObject", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
pluginId: denTypeIdColumn("plugin", "plugin_id").notNull(),
|
||||
configObjectId: denTypeIdColumn("configObject", "config_object_id").notNull(),
|
||||
membershipSource: mysqlEnum("membership_source", membershipSourceValues).notNull().default("manual"),
|
||||
connectorMappingId: denTypeIdColumn("connectorMapping", "connector_mapping_id"),
|
||||
createdByOrgMembershipId: denTypeIdColumn("member", "created_by_org_membership_id"),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
removedAt: timestamp("removed_at", { fsp: 3 }),
|
||||
},
|
||||
(table) => [
|
||||
index("plugin_config_object_organization_id").on(table.organizationId),
|
||||
index("plugin_config_object_plugin_id").on(table.pluginId),
|
||||
index("plugin_config_object_config_object_id").on(table.configObjectId),
|
||||
index("plugin_config_object_connector_mapping_id").on(table.connectorMappingId),
|
||||
uniqueIndex("plugin_config_object_plugin_config_object").on(table.pluginId, table.configObjectId),
|
||||
],
|
||||
)
|
||||
|
||||
export const ConfigObjectAccessGrantTable = mysqlTable(
|
||||
"config_object_access_grant",
|
||||
{
|
||||
id: denTypeIdColumn("configObjectAccessGrant", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
configObjectId: denTypeIdColumn("configObject", "config_object_id").notNull(),
|
||||
orgMembershipId: denTypeIdColumn("member", "org_membership_id"),
|
||||
teamId: denTypeIdColumn("team", "team_id"),
|
||||
orgWide: boolean("org_wide").notNull().default(false),
|
||||
role: mysqlEnum("role", accessRoleValues).notNull(),
|
||||
createdByOrgMembershipId: denTypeIdColumn("member", "created_by_org_membership_id").notNull(),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
removedAt: timestamp("removed_at", { fsp: 3 }),
|
||||
},
|
||||
(table) => [
|
||||
index("config_object_access_grant_organization_id").on(table.organizationId),
|
||||
index("config_object_access_grant_config_object_id").on(table.configObjectId),
|
||||
index("config_object_access_grant_org_membership_id").on(table.orgMembershipId),
|
||||
index("config_object_access_grant_team_id").on(table.teamId),
|
||||
index("config_object_access_grant_org_wide").on(table.orgWide),
|
||||
uniqueIndex("config_object_access_grant_object_org_membership").on(table.configObjectId, table.orgMembershipId),
|
||||
uniqueIndex("config_object_access_grant_object_team").on(table.configObjectId, table.teamId),
|
||||
],
|
||||
)
|
||||
|
||||
export const PluginAccessGrantTable = mysqlTable(
|
||||
"plugin_access_grant",
|
||||
{
|
||||
id: denTypeIdColumn("pluginAccessGrant", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
pluginId: denTypeIdColumn("plugin", "plugin_id").notNull(),
|
||||
orgMembershipId: denTypeIdColumn("member", "org_membership_id"),
|
||||
teamId: denTypeIdColumn("team", "team_id"),
|
||||
orgWide: boolean("org_wide").notNull().default(false),
|
||||
role: mysqlEnum("role", accessRoleValues).notNull(),
|
||||
createdByOrgMembershipId: denTypeIdColumn("member", "created_by_org_membership_id").notNull(),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
removedAt: timestamp("removed_at", { fsp: 3 }),
|
||||
},
|
||||
(table) => [
|
||||
index("plugin_access_grant_organization_id").on(table.organizationId),
|
||||
index("plugin_access_grant_plugin_id").on(table.pluginId),
|
||||
index("plugin_access_grant_org_membership_id").on(table.orgMembershipId),
|
||||
index("plugin_access_grant_team_id").on(table.teamId),
|
||||
index("plugin_access_grant_org_wide").on(table.orgWide),
|
||||
uniqueIndex("plugin_access_grant_plugin_org_membership").on(table.pluginId, table.orgMembershipId),
|
||||
uniqueIndex("plugin_access_grant_plugin_team").on(table.pluginId, table.teamId),
|
||||
],
|
||||
)
|
||||
|
||||
export const ConnectorAccountTable = mysqlTable(
|
||||
"connector_account",
|
||||
{
|
||||
id: denTypeIdColumn("connectorAccount", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
connectorType: mysqlEnum("connector_type", connectorTypeValues).notNull(),
|
||||
remoteId: varchar("remote_id", { length: 255 }).notNull(),
|
||||
externalAccountRef: varchar("external_account_ref", { length: 255 }),
|
||||
displayName: varchar("display_name", { length: 255 }).notNull(),
|
||||
status: mysqlEnum("status", connectorAccountStatusValues).notNull().default("active"),
|
||||
createdByOrgMembershipId: denTypeIdColumn("member", "created_by_org_membership_id").notNull(),
|
||||
metadataJson: json("metadata_json").$type<Record<string, unknown> | null>(),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { fsp: 3 }).notNull().default(sql`CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)`),
|
||||
},
|
||||
(table) => [
|
||||
index("connector_account_organization_id").on(table.organizationId),
|
||||
index("connector_account_created_by_org_membership_id").on(table.createdByOrgMembershipId),
|
||||
index("connector_account_connector_type").on(table.connectorType),
|
||||
index("connector_account_status").on(table.status),
|
||||
uniqueIndex("connector_account_org_type_remote_id").on(table.organizationId, table.connectorType, table.remoteId),
|
||||
],
|
||||
)
|
||||
|
||||
export const ConnectorInstanceTable = mysqlTable(
|
||||
"connector_instance",
|
||||
{
|
||||
id: denTypeIdColumn("connectorInstance", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
connectorAccountId: denTypeIdColumn("connectorAccount", "connector_account_id").notNull(),
|
||||
connectorType: mysqlEnum("connector_type", connectorTypeValues).notNull(),
|
||||
remoteId: varchar("remote_id", { length: 255 }),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
status: mysqlEnum("status", connectorInstanceStatusValues).notNull().default("active"),
|
||||
instanceConfigJson: json("instance_config_json").$type<Record<string, unknown> | null>(),
|
||||
lastSyncedAt: timestamp("last_synced_at", { fsp: 3 }),
|
||||
lastSyncStatus: mysqlEnum("last_sync_status", connectorSyncStatusValues),
|
||||
lastSyncCursor: text("last_sync_cursor"),
|
||||
createdByOrgMembershipId: denTypeIdColumn("member", "created_by_org_membership_id").notNull(),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { fsp: 3 }).notNull().default(sql`CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)`),
|
||||
},
|
||||
(table) => [
|
||||
index("connector_instance_organization_id").on(table.organizationId),
|
||||
index("connector_instance_connector_account_id").on(table.connectorAccountId),
|
||||
index("connector_instance_created_by_org_membership_id").on(table.createdByOrgMembershipId),
|
||||
index("connector_instance_connector_type").on(table.connectorType),
|
||||
index("connector_instance_status").on(table.status),
|
||||
uniqueIndex("connector_instance_org_name").on(table.organizationId, table.name),
|
||||
],
|
||||
)
|
||||
|
||||
export const ConnectorInstanceAccessGrantTable = mysqlTable(
|
||||
"connector_instance_access_grant",
|
||||
{
|
||||
id: denTypeIdColumn("connectorInstanceAccessGrant", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
connectorInstanceId: denTypeIdColumn("connectorInstance", "connector_instance_id").notNull(),
|
||||
orgMembershipId: denTypeIdColumn("member", "org_membership_id"),
|
||||
teamId: denTypeIdColumn("team", "team_id"),
|
||||
orgWide: boolean("org_wide").notNull().default(false),
|
||||
role: mysqlEnum("role", accessRoleValues).notNull(),
|
||||
createdByOrgMembershipId: denTypeIdColumn("member", "created_by_org_membership_id").notNull(),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
removedAt: timestamp("removed_at", { fsp: 3 }),
|
||||
},
|
||||
(table) => [
|
||||
index("connector_instance_access_grant_organization_id").on(table.organizationId),
|
||||
index("connector_instance_access_grant_instance_id").on(table.connectorInstanceId),
|
||||
index("connector_instance_access_grant_org_membership_id").on(table.orgMembershipId),
|
||||
index("connector_instance_access_grant_team_id").on(table.teamId),
|
||||
index("connector_instance_access_grant_org_wide").on(table.orgWide),
|
||||
uniqueIndex("connector_instance_access_grant_instance_org_membership").on(table.connectorInstanceId, table.orgMembershipId),
|
||||
uniqueIndex("connector_instance_access_grant_instance_team").on(table.connectorInstanceId, table.teamId),
|
||||
],
|
||||
)
|
||||
|
||||
export const ConnectorTargetTable = mysqlTable(
|
||||
"connector_target",
|
||||
{
|
||||
id: denTypeIdColumn("connectorTarget", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
connectorInstanceId: denTypeIdColumn("connectorInstance", "connector_instance_id").notNull(),
|
||||
connectorType: mysqlEnum("connector_type", connectorTypeValues).notNull(),
|
||||
remoteId: varchar("remote_id", { length: 255 }).notNull(),
|
||||
targetKind: mysqlEnum("target_kind", connectorTargetKindValues).notNull(),
|
||||
externalTargetRef: varchar("external_target_ref", { length: 255 }),
|
||||
targetConfigJson: json("target_config_json").$type<Record<string, unknown>>().notNull(),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { fsp: 3 }).notNull().default(sql`CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)`),
|
||||
},
|
||||
(table) => [
|
||||
index("connector_target_organization_id").on(table.organizationId),
|
||||
index("connector_target_connector_instance_id").on(table.connectorInstanceId),
|
||||
index("connector_target_connector_type").on(table.connectorType),
|
||||
index("connector_target_target_kind").on(table.targetKind),
|
||||
uniqueIndex("connector_target_instance_remote_id").on(table.connectorInstanceId, table.remoteId),
|
||||
],
|
||||
)
|
||||
|
||||
export const ConnectorMappingTable = mysqlTable(
|
||||
"connector_mapping",
|
||||
{
|
||||
id: denTypeIdColumn("connectorMapping", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
connectorInstanceId: denTypeIdColumn("connectorInstance", "connector_instance_id").notNull(),
|
||||
connectorTargetId: denTypeIdColumn("connectorTarget", "connector_target_id").notNull(),
|
||||
connectorType: mysqlEnum("connector_type", connectorTypeValues).notNull(),
|
||||
remoteId: varchar("remote_id", { length: 255 }),
|
||||
mappingKind: mysqlEnum("mapping_kind", connectorMappingKindValues).notNull(),
|
||||
selector: varchar("selector", { length: 255 }).notNull(),
|
||||
objectType: mysqlEnum("object_type", configObjectTypeValues).notNull(),
|
||||
pluginId: denTypeIdColumn("plugin", "plugin_id"),
|
||||
autoAddToPlugin: boolean("auto_add_to_plugin").notNull().default(false),
|
||||
mappingConfigJson: json("mapping_config_json").$type<Record<string, unknown> | null>(),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { fsp: 3 }).notNull().default(sql`CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)`),
|
||||
},
|
||||
(table) => [
|
||||
index("connector_mapping_organization_id").on(table.organizationId),
|
||||
index("connector_mapping_connector_instance_id").on(table.connectorInstanceId),
|
||||
index("connector_mapping_connector_target_id").on(table.connectorTargetId),
|
||||
index("connector_mapping_object_type").on(table.objectType),
|
||||
index("connector_mapping_plugin_id").on(table.pluginId),
|
||||
uniqueIndex("connector_mapping_target_selector_object_type").on(table.connectorTargetId, table.selector, table.objectType),
|
||||
],
|
||||
)
|
||||
|
||||
export const ConnectorSyncEventTable = mysqlTable(
|
||||
"connector_sync_event",
|
||||
{
|
||||
id: denTypeIdColumn("connectorSyncEvent", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
connectorInstanceId: denTypeIdColumn("connectorInstance", "connector_instance_id").notNull(),
|
||||
connectorTargetId: denTypeIdColumn("connectorTarget", "connector_target_id"),
|
||||
connectorType: mysqlEnum("connector_type", connectorTypeValues).notNull(),
|
||||
remoteId: varchar("remote_id", { length: 255 }),
|
||||
eventType: mysqlEnum("event_type", connectorSyncEventTypeValues).notNull(),
|
||||
externalEventRef: varchar("external_event_ref", { length: 255 }),
|
||||
sourceRevisionRef: varchar("source_revision_ref", { length: 255 }),
|
||||
status: mysqlEnum("status", connectorSyncStatusValues).notNull().default("pending"),
|
||||
summaryJson: json("summary_json").$type<Record<string, unknown> | null>(),
|
||||
startedAt: timestamp("started_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
completedAt: timestamp("completed_at", { fsp: 3 }),
|
||||
},
|
||||
(table) => [
|
||||
index("connector_sync_event_organization_id").on(table.organizationId),
|
||||
index("connector_sync_event_connector_instance_id").on(table.connectorInstanceId),
|
||||
index("connector_sync_event_connector_target_id").on(table.connectorTargetId),
|
||||
index("connector_sync_event_event_type").on(table.eventType),
|
||||
index("connector_sync_event_status").on(table.status),
|
||||
index("connector_sync_event_source_revision_ref").on(table.sourceRevisionRef),
|
||||
index("connector_sync_event_external_event_ref").on(table.externalEventRef),
|
||||
],
|
||||
)
|
||||
|
||||
export const ConnectorSourceBindingTable = mysqlTable(
|
||||
"connector_source_binding",
|
||||
{
|
||||
id: denTypeIdColumn("connectorSourceBinding", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
configObjectId: denTypeIdColumn("configObject", "config_object_id").notNull(),
|
||||
connectorInstanceId: denTypeIdColumn("connectorInstance", "connector_instance_id").notNull(),
|
||||
connectorTargetId: denTypeIdColumn("connectorTarget", "connector_target_id").notNull(),
|
||||
connectorMappingId: denTypeIdColumn("connectorMapping", "connector_mapping_id").notNull(),
|
||||
connectorType: mysqlEnum("connector_type", connectorTypeValues).notNull(),
|
||||
remoteId: varchar("remote_id", { length: 255 }),
|
||||
externalLocator: varchar("external_locator", { length: 255 }).notNull(),
|
||||
externalStableRef: varchar("external_stable_ref", { length: 255 }),
|
||||
lastSeenSourceRevisionRef: varchar("last_seen_source_revision_ref", { length: 255 }),
|
||||
status: mysqlEnum("status", configObjectStatusValues).notNull().default("active"),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { fsp: 3 }).notNull().default(sql`CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)`),
|
||||
deletedAt: timestamp("deleted_at", { fsp: 3 }),
|
||||
},
|
||||
(table) => [
|
||||
index("connector_source_binding_organization_id").on(table.organizationId),
|
||||
index("connector_source_binding_config_object_id").on(table.configObjectId),
|
||||
index("connector_source_binding_connector_instance_id").on(table.connectorInstanceId),
|
||||
index("connector_source_binding_connector_target_id").on(table.connectorTargetId),
|
||||
index("connector_source_binding_connector_mapping_id").on(table.connectorMappingId),
|
||||
index("connector_source_binding_external_locator").on(table.externalLocator),
|
||||
uniqueIndex("connector_source_binding_config_object").on(table.configObjectId),
|
||||
],
|
||||
)
|
||||
|
||||
export const ConnectorSourceTombstoneTable = mysqlTable(
|
||||
"connector_source_tombstone",
|
||||
{
|
||||
id: denTypeIdColumn("connectorSourceTombstone", "id").notNull().primaryKey(),
|
||||
organizationId: denTypeIdColumn("organization", "organization_id").notNull(),
|
||||
connectorInstanceId: denTypeIdColumn("connectorInstance", "connector_instance_id").notNull(),
|
||||
connectorTargetId: denTypeIdColumn("connectorTarget", "connector_target_id").notNull(),
|
||||
connectorMappingId: denTypeIdColumn("connectorMapping", "connector_mapping_id").notNull(),
|
||||
connectorType: mysqlEnum("connector_type", connectorTypeValues).notNull(),
|
||||
remoteId: varchar("remote_id", { length: 255 }),
|
||||
externalLocator: varchar("external_locator", { length: 255 }).notNull(),
|
||||
formerConfigObjectId: denTypeIdColumn("configObject", "former_config_object_id").notNull(),
|
||||
deletedInSyncEventId: denTypeIdColumn("connectorSyncEvent", "deleted_in_sync_event_id").notNull(),
|
||||
deletedSourceRevisionRef: varchar("deleted_source_revision_ref", { length: 255 }),
|
||||
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
||||
},
|
||||
(table) => [
|
||||
index("connector_source_tombstone_organization_id").on(table.organizationId),
|
||||
index("connector_source_tombstone_connector_instance_id").on(table.connectorInstanceId),
|
||||
index("connector_source_tombstone_connector_target_id").on(table.connectorTargetId),
|
||||
index("connector_source_tombstone_connector_mapping_id").on(table.connectorMappingId),
|
||||
index("connector_source_tombstone_external_locator").on(table.externalLocator),
|
||||
index("connector_source_tombstone_former_config_object_id").on(table.formerConfigObjectId),
|
||||
],
|
||||
)
|
||||
|
||||
export const configObjectRelations = relations(ConfigObjectTable, ({ many, one }) => ({
|
||||
accessGrants: many(ConfigObjectAccessGrantTable),
|
||||
connectorInstance: one(ConnectorInstanceTable, {
|
||||
fields: [ConfigObjectTable.connectorInstanceId],
|
||||
references: [ConnectorInstanceTable.id],
|
||||
}),
|
||||
createdByOrgMembership: one(MemberTable, {
|
||||
fields: [ConfigObjectTable.createdByOrgMembershipId],
|
||||
references: [MemberTable.id],
|
||||
}),
|
||||
memberships: many(PluginConfigObjectTable),
|
||||
organization: one(OrganizationTable, {
|
||||
fields: [ConfigObjectTable.organizationId],
|
||||
references: [OrganizationTable.id],
|
||||
}),
|
||||
sourceBindings: many(ConnectorSourceBindingTable),
|
||||
versions: many(ConfigObjectVersionTable),
|
||||
}))
|
||||
|
||||
export const configObjectVersionRelations = relations(ConfigObjectVersionTable, ({ one }) => ({
|
||||
configObject: one(ConfigObjectTable, {
|
||||
fields: [ConfigObjectVersionTable.configObjectId],
|
||||
references: [ConfigObjectTable.id],
|
||||
}),
|
||||
connectorSyncEvent: one(ConnectorSyncEventTable, {
|
||||
fields: [ConfigObjectVersionTable.connectorSyncEventId],
|
||||
references: [ConnectorSyncEventTable.id],
|
||||
}),
|
||||
createdByOrgMembership: one(MemberTable, {
|
||||
fields: [ConfigObjectVersionTable.createdByOrgMembershipId],
|
||||
references: [MemberTable.id],
|
||||
}),
|
||||
}))
|
||||
|
||||
export const pluginRelations = relations(PluginTable, ({ many, one }) => ({
|
||||
accessGrants: many(PluginAccessGrantTable),
|
||||
createdByOrgMembership: one(MemberTable, {
|
||||
fields: [PluginTable.createdByOrgMembershipId],
|
||||
references: [MemberTable.id],
|
||||
}),
|
||||
memberships: many(PluginConfigObjectTable),
|
||||
organization: one(OrganizationTable, {
|
||||
fields: [PluginTable.organizationId],
|
||||
references: [OrganizationTable.id],
|
||||
}),
|
||||
mappings: many(ConnectorMappingTable),
|
||||
}))
|
||||
|
||||
export const pluginConfigObjectRelations = relations(PluginConfigObjectTable, ({ one }) => ({
|
||||
configObject: one(ConfigObjectTable, {
|
||||
fields: [PluginConfigObjectTable.configObjectId],
|
||||
references: [ConfigObjectTable.id],
|
||||
}),
|
||||
connectorMapping: one(ConnectorMappingTable, {
|
||||
fields: [PluginConfigObjectTable.connectorMappingId],
|
||||
references: [ConnectorMappingTable.id],
|
||||
}),
|
||||
createdByOrgMembership: one(MemberTable, {
|
||||
fields: [PluginConfigObjectTable.createdByOrgMembershipId],
|
||||
references: [MemberTable.id],
|
||||
}),
|
||||
plugin: one(PluginTable, {
|
||||
fields: [PluginConfigObjectTable.pluginId],
|
||||
references: [PluginTable.id],
|
||||
}),
|
||||
}))
|
||||
|
||||
export const configObjectAccessGrantRelations = relations(ConfigObjectAccessGrantTable, ({ one }) => ({
|
||||
configObject: one(ConfigObjectTable, {
|
||||
fields: [ConfigObjectAccessGrantTable.configObjectId],
|
||||
references: [ConfigObjectTable.id],
|
||||
}),
|
||||
createdByOrgMembership: one(MemberTable, {
|
||||
fields: [ConfigObjectAccessGrantTable.createdByOrgMembershipId],
|
||||
references: [MemberTable.id],
|
||||
}),
|
||||
orgMembership: one(MemberTable, {
|
||||
fields: [ConfigObjectAccessGrantTable.orgMembershipId],
|
||||
references: [MemberTable.id],
|
||||
}),
|
||||
team: one(TeamTable, {
|
||||
fields: [ConfigObjectAccessGrantTable.teamId],
|
||||
references: [TeamTable.id],
|
||||
}),
|
||||
}))
|
||||
|
||||
export const pluginAccessGrantRelations = relations(PluginAccessGrantTable, ({ one }) => ({
|
||||
createdByOrgMembership: one(MemberTable, {
|
||||
fields: [PluginAccessGrantTable.createdByOrgMembershipId],
|
||||
references: [MemberTable.id],
|
||||
}),
|
||||
orgMembership: one(MemberTable, {
|
||||
fields: [PluginAccessGrantTable.orgMembershipId],
|
||||
references: [MemberTable.id],
|
||||
}),
|
||||
plugin: one(PluginTable, {
|
||||
fields: [PluginAccessGrantTable.pluginId],
|
||||
references: [PluginTable.id],
|
||||
}),
|
||||
team: one(TeamTable, {
|
||||
fields: [PluginAccessGrantTable.teamId],
|
||||
references: [TeamTable.id],
|
||||
}),
|
||||
}))
|
||||
|
||||
export const connectorAccountRelations = relations(ConnectorAccountTable, ({ many, one }) => ({
|
||||
createdByOrgMembership: one(MemberTable, {
|
||||
fields: [ConnectorAccountTable.createdByOrgMembershipId],
|
||||
references: [MemberTable.id],
|
||||
}),
|
||||
instances: many(ConnectorInstanceTable),
|
||||
organization: one(OrganizationTable, {
|
||||
fields: [ConnectorAccountTable.organizationId],
|
||||
references: [OrganizationTable.id],
|
||||
}),
|
||||
}))
|
||||
|
||||
export const connectorInstanceRelations = relations(ConnectorInstanceTable, ({ many, one }) => ({
|
||||
accessGrants: many(ConnectorInstanceAccessGrantTable),
|
||||
account: one(ConnectorAccountTable, {
|
||||
fields: [ConnectorInstanceTable.connectorAccountId],
|
||||
references: [ConnectorAccountTable.id],
|
||||
}),
|
||||
configObjects: many(ConfigObjectTable),
|
||||
createdByOrgMembership: one(MemberTable, {
|
||||
fields: [ConnectorInstanceTable.createdByOrgMembershipId],
|
||||
references: [MemberTable.id],
|
||||
}),
|
||||
mappings: many(ConnectorMappingTable),
|
||||
organization: one(OrganizationTable, {
|
||||
fields: [ConnectorInstanceTable.organizationId],
|
||||
references: [OrganizationTable.id],
|
||||
}),
|
||||
sourceBindings: many(ConnectorSourceBindingTable),
|
||||
syncEvents: many(ConnectorSyncEventTable),
|
||||
targets: many(ConnectorTargetTable),
|
||||
tombstones: many(ConnectorSourceTombstoneTable),
|
||||
}))
|
||||
|
||||
export const connectorInstanceAccessGrantRelations = relations(ConnectorInstanceAccessGrantTable, ({ one }) => ({
|
||||
connectorInstance: one(ConnectorInstanceTable, {
|
||||
fields: [ConnectorInstanceAccessGrantTable.connectorInstanceId],
|
||||
references: [ConnectorInstanceTable.id],
|
||||
}),
|
||||
createdByOrgMembership: one(MemberTable, {
|
||||
fields: [ConnectorInstanceAccessGrantTable.createdByOrgMembershipId],
|
||||
references: [MemberTable.id],
|
||||
}),
|
||||
orgMembership: one(MemberTable, {
|
||||
fields: [ConnectorInstanceAccessGrantTable.orgMembershipId],
|
||||
references: [MemberTable.id],
|
||||
}),
|
||||
team: one(TeamTable, {
|
||||
fields: [ConnectorInstanceAccessGrantTable.teamId],
|
||||
references: [TeamTable.id],
|
||||
}),
|
||||
}))
|
||||
|
||||
export const connectorTargetRelations = relations(ConnectorTargetTable, ({ many, one }) => ({
|
||||
connectorInstance: one(ConnectorInstanceTable, {
|
||||
fields: [ConnectorTargetTable.connectorInstanceId],
|
||||
references: [ConnectorInstanceTable.id],
|
||||
}),
|
||||
mappings: many(ConnectorMappingTable),
|
||||
sourceBindings: many(ConnectorSourceBindingTable),
|
||||
syncEvents: many(ConnectorSyncEventTable),
|
||||
tombstones: many(ConnectorSourceTombstoneTable),
|
||||
}))
|
||||
|
||||
export const connectorMappingRelations = relations(ConnectorMappingTable, ({ many, one }) => ({
|
||||
connectorInstance: one(ConnectorInstanceTable, {
|
||||
fields: [ConnectorMappingTable.connectorInstanceId],
|
||||
references: [ConnectorInstanceTable.id],
|
||||
}),
|
||||
connectorTarget: one(ConnectorTargetTable, {
|
||||
fields: [ConnectorMappingTable.connectorTargetId],
|
||||
references: [ConnectorTargetTable.id],
|
||||
}),
|
||||
plugin: one(PluginTable, {
|
||||
fields: [ConnectorMappingTable.pluginId],
|
||||
references: [PluginTable.id],
|
||||
}),
|
||||
pluginMemberships: many(PluginConfigObjectTable),
|
||||
sourceBindings: many(ConnectorSourceBindingTable),
|
||||
tombstones: many(ConnectorSourceTombstoneTable),
|
||||
}))
|
||||
|
||||
export const connectorSyncEventRelations = relations(ConnectorSyncEventTable, ({ many, one }) => ({
|
||||
connectorInstance: one(ConnectorInstanceTable, {
|
||||
fields: [ConnectorSyncEventTable.connectorInstanceId],
|
||||
references: [ConnectorInstanceTable.id],
|
||||
}),
|
||||
connectorTarget: one(ConnectorTargetTable, {
|
||||
fields: [ConnectorSyncEventTable.connectorTargetId],
|
||||
references: [ConnectorTargetTable.id],
|
||||
}),
|
||||
tombstones: many(ConnectorSourceTombstoneTable),
|
||||
versions: many(ConfigObjectVersionTable),
|
||||
}))
|
||||
|
||||
export const connectorSourceBindingRelations = relations(ConnectorSourceBindingTable, ({ one }) => ({
|
||||
configObject: one(ConfigObjectTable, {
|
||||
fields: [ConnectorSourceBindingTable.configObjectId],
|
||||
references: [ConfigObjectTable.id],
|
||||
}),
|
||||
connectorInstance: one(ConnectorInstanceTable, {
|
||||
fields: [ConnectorSourceBindingTable.connectorInstanceId],
|
||||
references: [ConnectorInstanceTable.id],
|
||||
}),
|
||||
connectorMapping: one(ConnectorMappingTable, {
|
||||
fields: [ConnectorSourceBindingTable.connectorMappingId],
|
||||
references: [ConnectorMappingTable.id],
|
||||
}),
|
||||
connectorTarget: one(ConnectorTargetTable, {
|
||||
fields: [ConnectorSourceBindingTable.connectorTargetId],
|
||||
references: [ConnectorTargetTable.id],
|
||||
}),
|
||||
}))
|
||||
|
||||
export const connectorSourceTombstoneRelations = relations(ConnectorSourceTombstoneTable, ({ one }) => ({
|
||||
connectorInstance: one(ConnectorInstanceTable, {
|
||||
fields: [ConnectorSourceTombstoneTable.connectorInstanceId],
|
||||
references: [ConnectorInstanceTable.id],
|
||||
}),
|
||||
connectorMapping: one(ConnectorMappingTable, {
|
||||
fields: [ConnectorSourceTombstoneTable.connectorMappingId],
|
||||
references: [ConnectorMappingTable.id],
|
||||
}),
|
||||
connectorTarget: one(ConnectorTargetTable, {
|
||||
fields: [ConnectorSourceTombstoneTable.connectorTargetId],
|
||||
references: [ConnectorTargetTable.id],
|
||||
}),
|
||||
deletedInSyncEvent: one(ConnectorSyncEventTable, {
|
||||
fields: [ConnectorSourceTombstoneTable.deletedInSyncEventId],
|
||||
references: [ConnectorSyncEventTable.id],
|
||||
}),
|
||||
formerConfigObject: one(ConfigObjectTable, {
|
||||
fields: [ConnectorSourceTombstoneTable.formerConfigObjectId],
|
||||
references: [ConfigObjectTable.id],
|
||||
}),
|
||||
}))
|
||||
|
||||
export const configObject = ConfigObjectTable
|
||||
export const configObjectVersion = ConfigObjectVersionTable
|
||||
export const plugin = PluginTable
|
||||
export const pluginConfigObject = PluginConfigObjectTable
|
||||
export const configObjectAccessGrant = ConfigObjectAccessGrantTable
|
||||
export const pluginAccessGrant = PluginAccessGrantTable
|
||||
export const connectorAccount = ConnectorAccountTable
|
||||
export const connectorInstance = ConnectorInstanceTable
|
||||
export const connectorInstanceAccessGrant = ConnectorInstanceAccessGrantTable
|
||||
export const connectorTarget = ConnectorTargetTable
|
||||
export const connectorMapping = ConnectorMappingTable
|
||||
export const connectorSyncEvent = ConnectorSyncEventTable
|
||||
export const connectorSourceBinding = ConnectorSourceBindingTable
|
||||
export const connectorSourceTombstone = ConnectorSourceTombstoneTable
|
||||
@@ -26,6 +26,20 @@ export const idTypesMapNameToPrefix = {
|
||||
skillHub: "shb",
|
||||
skillHubSkill: "shs",
|
||||
skillHubMember: "shm",
|
||||
configObject: "cob",
|
||||
configObjectVersion: "cov",
|
||||
configObjectAccessGrant: "coa",
|
||||
plugin: "plg",
|
||||
pluginConfigObject: "pco",
|
||||
pluginAccessGrant: "pag",
|
||||
connectorAccount: "cac",
|
||||
connectorInstance: "cin",
|
||||
connectorInstanceAccessGrant: "cia",
|
||||
connectorTarget: "ctg",
|
||||
connectorMapping: "cmp",
|
||||
connectorSyncEvent: "cse",
|
||||
connectorSourceBinding: "csb",
|
||||
connectorSourceTombstone: "cst",
|
||||
llmProvider: "lpr",
|
||||
llmProviderModel: "lpm",
|
||||
llmProviderAccess: "lpa",
|
||||
|
||||
Reference in New Issue
Block a user