mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-18 02:11:59 -05:00
regen migrations, ack PR comments
This commit is contained in:
@@ -117,11 +117,12 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
}
|
||||
|
||||
let finalSourceConfig: Record<string, unknown> = sourceConfig
|
||||
const tagSlotMapping: 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 tagSlotMapping: Record<string, string> = {}
|
||||
const skippedTags: string[] = []
|
||||
for (const td of enabledDefs) {
|
||||
const slot = await getNextAvailableSlot(knowledgeBaseId, td.fieldType)
|
||||
@@ -130,15 +131,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
logger.warn(`[${requestId}] No available ${td.fieldType} slots for "${td.displayName}"`)
|
||||
continue
|
||||
}
|
||||
await createTagDefinition(
|
||||
{
|
||||
knowledgeBaseId,
|
||||
tagSlot: slot,
|
||||
displayName: td.displayName,
|
||||
fieldType: td.fieldType,
|
||||
},
|
||||
requestId
|
||||
)
|
||||
tagSlotMapping[td.id] = slot
|
||||
}
|
||||
|
||||
@@ -157,17 +149,33 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
const nextSyncAt =
|
||||
syncIntervalMinutes > 0 ? new Date(now.getTime() + syncIntervalMinutes * 60 * 1000) : null
|
||||
|
||||
await db.insert(knowledgeConnector).values({
|
||||
id: connectorId,
|
||||
knowledgeBaseId,
|
||||
connectorType,
|
||||
credentialId,
|
||||
sourceConfig: finalSourceConfig,
|
||||
syncIntervalMinutes,
|
||||
status: 'active',
|
||||
nextSyncAt,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
await db.transaction(async (tx) => {
|
||||
for (const [semanticId, slot] of Object.entries(tagSlotMapping)) {
|
||||
const td = connectorConfig.tagDefinitions!.find((d) => d.id === semanticId)!
|
||||
await createTagDefinition(
|
||||
{
|
||||
knowledgeBaseId,
|
||||
tagSlot: slot,
|
||||
displayName: td.displayName,
|
||||
fieldType: td.fieldType,
|
||||
},
|
||||
requestId,
|
||||
tx
|
||||
)
|
||||
}
|
||||
|
||||
await tx.insert(knowledgeConnector).values({
|
||||
id: connectorId,
|
||||
knowledgeBaseId,
|
||||
connectorType,
|
||||
credentialId,
|
||||
sourceConfig: finalSourceConfig,
|
||||
syncIntervalMinutes,
|
||||
status: 'active',
|
||||
nextSyncAt,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
})
|
||||
})
|
||||
|
||||
logger.info(`[${requestId}] Created connector ${connectorId} for KB ${knowledgeBaseId}`)
|
||||
|
||||
@@ -745,6 +745,7 @@ export function Document({
|
||||
setEnabledFilter('all')
|
||||
setIsFilterPopoverOpen(false)
|
||||
setSelectedChunks(new Set())
|
||||
goToPage(1)
|
||||
}}
|
||||
>
|
||||
All
|
||||
@@ -755,6 +756,7 @@ export function Document({
|
||||
setEnabledFilter('enabled')
|
||||
setIsFilterPopoverOpen(false)
|
||||
setSelectedChunks(new Set())
|
||||
goToPage(1)
|
||||
}}
|
||||
>
|
||||
Enabled
|
||||
@@ -765,6 +767,7 @@ export function Document({
|
||||
setEnabledFilter('disabled')
|
||||
setIsFilterPopoverOpen(false)
|
||||
setSelectedChunks(new Set())
|
||||
goToPage(1)
|
||||
}}
|
||||
>
|
||||
Disabled
|
||||
|
||||
@@ -8,13 +8,6 @@ const logger = createLogger('JiraConnector')
|
||||
|
||||
const PAGE_SIZE = 50
|
||||
|
||||
/**
|
||||
* Escapes a value for use inside JQL double-quoted strings.
|
||||
*/
|
||||
function escapeJql(value: string): string {
|
||||
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a SHA-256 hash of the given content.
|
||||
*/
|
||||
@@ -147,9 +140,9 @@ export const jiraConnector: ConnectorConfig = {
|
||||
|
||||
const cloudId = await getJiraCloudId(domain, accessToken)
|
||||
|
||||
let jql = `project = "${escapeJql(projectKey)}" ORDER BY updated DESC`
|
||||
let jql = `project = ${projectKey} ORDER BY updated DESC`
|
||||
if (jqlFilter.trim()) {
|
||||
jql = `project = "${escapeJql(projectKey)}" AND (${jqlFilter.trim()}) ORDER BY updated DESC`
|
||||
jql = `project = ${projectKey} AND (${jqlFilter.trim()}) ORDER BY updated DESC`
|
||||
}
|
||||
|
||||
const startAt = cursor ? Number(cursor) : 0
|
||||
@@ -251,19 +244,13 @@ export const jiraConnector: ConnectorConfig = {
|
||||
return { valid: false, error: 'Max issues must be a positive number' }
|
||||
}
|
||||
|
||||
const jql = sourceConfig.jql as string | undefined
|
||||
if (jql?.trim()) {
|
||||
if (/\b(delete|drop|truncate|insert|update|alter|create|grant|revoke)\b/i.test(jql)) {
|
||||
return { valid: false, error: 'Invalid JQL filter' }
|
||||
}
|
||||
}
|
||||
const jqlFilter = (sourceConfig.jql as string | undefined)?.trim() || ''
|
||||
|
||||
try {
|
||||
const cloudId = await getJiraCloudId(domain, accessToken)
|
||||
|
||||
// Verify the project exists by running a minimal search
|
||||
const params = new URLSearchParams()
|
||||
params.append('jql', `project = "${escapeJql(projectKey)}"`)
|
||||
params.append('jql', `project = ${projectKey}`)
|
||||
params.append('maxResults', '0')
|
||||
|
||||
const url = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/search?${params.toString()}`
|
||||
@@ -287,6 +274,29 @@ export const jiraConnector: ConnectorConfig = {
|
||||
return { valid: false, error: `Failed to validate: ${response.status} - ${errorText}` }
|
||||
}
|
||||
|
||||
if (jqlFilter) {
|
||||
const filterParams = new URLSearchParams()
|
||||
filterParams.append('jql', `project = ${projectKey} AND (${jqlFilter})`)
|
||||
filterParams.append('maxResults', '0')
|
||||
|
||||
const filterUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/search?${filterParams.toString()}`
|
||||
const filterResponse = await fetchWithRetry(
|
||||
filterUrl,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
},
|
||||
VALIDATE_RETRY_OPTIONS
|
||||
)
|
||||
|
||||
if (!filterResponse.ok) {
|
||||
return { valid: false, error: 'Invalid JQL filter. Check syntax and field names.' }
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: true }
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Failed to validate configuration'
|
||||
|
||||
@@ -275,7 +275,7 @@ export async function executeSync(
|
||||
}
|
||||
}
|
||||
|
||||
if (options?.fullSync || connector.syncMode === 'incremental') {
|
||||
if (options?.fullSync || connector.syncMode === 'full') {
|
||||
for (const existing of existingDocs) {
|
||||
if (existing.externalId && !seenExternalIds.has(existing.externalId)) {
|
||||
await db
|
||||
|
||||
@@ -3,6 +3,7 @@ import { db } from '@sim/db'
|
||||
import { document, embedding, knowledgeBaseTagDefinitions } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq, isNotNull, isNull, sql } from 'drizzle-orm'
|
||||
import type { DbOrTx } from '@/lib/db/types'
|
||||
import { getSlotsForFieldType, SUPPORTED_FIELD_TYPES } from '@/lib/knowledge/constants'
|
||||
import type { BulkTagDefinitionsData, DocumentTagDefinition } from '@/lib/knowledge/tags/types'
|
||||
import type {
|
||||
@@ -485,8 +486,10 @@ export async function deleteTagDefinition(
|
||||
*/
|
||||
export async function createTagDefinition(
|
||||
data: CreateTagDefinitionData,
|
||||
requestId: string
|
||||
requestId: string,
|
||||
txDb?: DbOrTx
|
||||
): Promise<TagDefinition> {
|
||||
const dbInstance = txDb ?? db
|
||||
const tagDefinitionId = randomUUID()
|
||||
const now = new Date()
|
||||
|
||||
@@ -500,7 +503,7 @@ export async function createTagDefinition(
|
||||
updatedAt: now,
|
||||
}
|
||||
|
||||
await db.insert(knowledgeBaseTagDefinitions).values(newDefinition)
|
||||
await dbInstance.insert(knowledgeBaseTagDefinitions).values(newDefinition)
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Created tag definition: ${data.displayName} -> ${data.tagSlot} in KB ${data.knowledgeBaseId}`
|
||||
|
||||
Reference in New Issue
Block a user