fix(kb): fix Linear connector GraphQL type errors and tag slot reuse (#3961)

* fix(kb): fix Linear connector GraphQL type errors and tag slot reuse

* fix(kb): simplify tag slot reuse, revert Linear GraphQL types to String

Clean up newTagSlotMapping into direct assignment, remove unnecessary
comment, and revert ID! back to String! to match Linear SDK types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(kb): use ID! type for Linear GraphQL filter variables

* fix(kb): verify field type when reusing existing tag slots

Add fieldType check to the tag slot reuse logic so a connector with
a matching displayName but different fieldType falls through to fresh
slot allocation instead of silently reusing an incompatible slot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(kb): enable search on connector selector dropdowns

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Waleed
2026-04-04 16:50:04 -07:00
committed by GitHub
co-authored by Claude Opus 4.6
parent b4d9b8c396
commit a70ccddef5
3 changed files with 28 additions and 6 deletions
@@ -162,19 +162,39 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
}
const tagSlotMapping: Record<string, string> = {}
let newTagSlots: Record<string, string> = {}
if (connectorConfig.tagDefinitions?.length) {
const disabledIds = new Set((sourceConfig.disabledTagIds as string[] | undefined) ?? [])
const enabledDefs = connectorConfig.tagDefinitions.filter((td) => !disabledIds.has(td.id))
const existingDefs = await db
.select({ tagSlot: knowledgeBaseTagDefinitions.tagSlot })
.select({
tagSlot: knowledgeBaseTagDefinitions.tagSlot,
displayName: knowledgeBaseTagDefinitions.displayName,
fieldType: knowledgeBaseTagDefinitions.fieldType,
})
.from(knowledgeBaseTagDefinitions)
.where(eq(knowledgeBaseTagDefinitions.knowledgeBaseId, knowledgeBaseId))
const usedSlots = new Set<string>(existingDefs.map((d) => d.tagSlot))
const { mapping, skipped: skippedTags } = allocateTagSlots(enabledDefs, usedSlots)
const existingByName = new Map(
existingDefs.map((d) => [d.displayName, { tagSlot: d.tagSlot, fieldType: d.fieldType }])
)
const defsNeedingSlots: typeof enabledDefs = []
for (const td of enabledDefs) {
const existing = existingByName.get(td.displayName)
if (existing && existing.fieldType === td.fieldType) {
tagSlotMapping[td.id] = existing.tagSlot
} else {
defsNeedingSlots.push(td)
}
}
const { mapping, skipped: skippedTags } = allocateTagSlots(defsNeedingSlots, usedSlots)
Object.assign(tagSlotMapping, mapping)
newTagSlots = mapping
for (const name of skippedTags) {
logger.warn(`[${requestId}] No available slots for "${name}"`)
@@ -208,7 +228,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
throw new Error('Knowledge base not found')
}
for (const [semanticId, slot] of Object.entries(tagSlotMapping)) {
for (const [semanticId, slot] of Object.entries(newTagSlots)) {
const td = connectorConfig.tagDefinitions!.find((d) => d.id === semanticId)!
await createTagDefinition(
{
@@ -79,6 +79,8 @@ export function ConnectorSelectorField({
options={comboboxOptions}
value={value || undefined}
onChange={onChange}
searchable
searchPlaceholder={`Search ${field.title.toLowerCase()}...`}
placeholder={
!credentialId
? 'Connect an account first'
+3 -3
View File
@@ -119,7 +119,7 @@ const ISSUE_FIELDS = `
`
const ISSUE_BY_ID_QUERY = `
query GetIssue($id: String!) {
query GetIssue($id: ID!) {
issue(id: $id) {
${ISSUE_FIELDS}
}
@@ -147,13 +147,13 @@ function buildIssuesQuery(sourceConfig: Record<string, unknown>): {
const variables: Record<string, unknown> = {}
if (teamId) {
varDefs.push('$teamId: String!')
varDefs.push('$teamId: ID!')
filterClauses.push('team: { id: { eq: $teamId } }')
variables.teamId = teamId
}
if (projectId) {
varDefs.push('$projectId: String!')
varDefs.push('$projectId: ID!')
filterClauses.push('project: { id: { eq: $projectId } }')
variables.projectId = projectId
}