Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,16 @@ const DeployVariantModalContent = ({variantName, revision, isLoading}: any) => {
return (
<section className="flex flex-col gap-4" data-tour="deploy-variant-modal">
<Typography.Text>
Select an environment to deploy <span className="font-medium">{variantName}</span>{" "}
Select environments to deploy{" "}
<span className="font-medium">{variantName}</span>{" "}
{typeof revision !== "undefined" && (
<VersionBadge version={revision} variant="chip" />
)}
</Typography.Text>

<Table
rowSelection={{
type: "radio",
type: "checkbox",
columnWidth: 48,
selectedRowKeys: selectedEnvName,
onChange: (selectedRowKeys: React.Key[]) => {
Expand All @@ -76,7 +77,11 @@ const DeployVariantModalContent = ({variantName, revision, isLoading}: any) => {
onRow={(env) => ({
className: "cursor-pointer",
onClick: () => {
setSelectedEnvName([env.name])
setSelectedEnvName((prev) =>
prev.includes(env.name)
? prev.filter((n) => n !== env.name)
: [...prev, env.name],
)
},
})}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,15 @@ const DeployVariantModal = ({
if (result?.error) message.error(result.error)
return
}
const env = result.env as string
const envs = result.envs as string[]
onClose()
message.success(`Published ${variantName} to ${env}`)
posthog?.capture?.("app_deployed", {app_id: appId, environment: env})
message.success(`Published ${variantName} to ${envs.join(", ")}`)
posthog?.capture?.("app_deployed", {app_id: appId, environment: envs.join(",")})
recordWidgetEvent("variant_deployed")
if (result.error) {
// Partial success: some environments failed
message.warning(result.error)
}
}, [submitDeploy, onClose, variantName, appId, posthog, recordWidgetEvent])

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ export const deployResetAtom = atom(null, (get, set) => {
})

// Async submit action. Optional overrides allow providing ids directly.
// Returns { ok: boolean, env?: string, error?: string }
// Deploys to all selected environments.
// Returns { ok: boolean, envs?: string[], error?: string }
export const deploySubmitAtom = atom(
null,
async (get, set, overrides?: {parentVariantId?: string | null; revisionId?: string | null}) => {
Expand All @@ -73,7 +74,7 @@ export const deploySubmitAtom = atom(
parentVariantId: overrides?.parentVariantId ?? baseState.parentVariantId ?? null,
revisionId: overrides?.revisionId ?? baseState.revisionId ?? null,
}
const selectedEnvName = get(deploySelectedEnvAtom)
const selectedEnvNames = get(deploySelectedEnvAtom)
const note = get(deployNoteAtom)
const appId = get(routerAppIdAtom)
const {mutateAsync: publish} = get(publishMutationAtom)
Expand All @@ -82,12 +83,11 @@ export const deploySubmitAtom = atom(
console.debug("[DeployModal] submit:start", {
state,
overrides,
selectedEnvName,
selectedEnvNames,
note,
})

const env = selectedEnvName[0]
if (!env) {
if (!selectedEnvNames.length) {
console.debug("[DeployModal] submit:fail", {reason: "no_env_selected"})
return {ok: false, error: "No environment selected"}
}
Expand Down Expand Up @@ -116,36 +116,49 @@ export const deploySubmitAtom = atom(
const workflowEntity = workflows.find((w) => w.id === workflowId)
const applicationSlug = workflowEntity?.slug || undefined

try {
console.debug("[DeployModal] submit:publish", {
revisionId,
env,
workflowData: workflowData
? {
workflow_id: workflowData.workflow_id,
workflow_variant_id: workflowData.workflow_variant_id,
slug: workflowData.slug,
resolvedVariantSlug,
applicationSlug,
}
: null,
})
await publish({
revisionId,
environmentSlug: env,
applicationId: workflowId || appId || "",
workflowVariantId: workflowData?.workflow_variant_id ?? undefined,
variantSlug: resolvedVariantSlug ?? undefined,
applicationSlug,
revisionVersion: workflowData?.version ?? undefined,
note,
})
console.debug("[DeployModal] submit:success", {env})
return {ok: true, env}
} catch (e: unknown) {
const errorMessage = e instanceof Error ? e.message : "Failed to deploy"
console.debug("[DeployModal] submit:error", {error: e})
return {ok: false, error: errorMessage}
const succeeded: string[] = []
const errors: string[] = []

for (const env of selectedEnvNames) {
try {
console.debug("[DeployModal] submit:publish", {
revisionId,
env,
workflowData: workflowData
? {
workflow_id: workflowData.workflow_id,
workflow_variant_id: workflowData.workflow_variant_id,
slug: workflowData.slug,
resolvedVariantSlug,
applicationSlug,
}
: null,
})
await publish({
revisionId,
environmentSlug: env,
applicationId: workflowId || appId || "",
workflowVariantId: workflowData?.workflow_variant_id ?? undefined,
variantSlug: resolvedVariantSlug ?? undefined,
applicationSlug,
revisionVersion: workflowData?.version ?? undefined,
note,
})
console.debug("[DeployModal] submit:success", {env})
succeeded.push(env)
} catch (e: unknown) {
const errorMessage = e instanceof Error ? e.message : "Failed to deploy"
console.debug("[DeployModal] submit:error", {env, error: e})
errors.push(`${env}: ${errorMessage}`)
}
}

if (succeeded.length && !errors.length) {
return {ok: true, envs: succeeded}
} else if (succeeded.length && errors.length) {
return {ok: true, envs: succeeded, error: `Failed for: ${errors.join("; ")}`}
} else {
return {ok: false, error: errors.join("; ")}
}
},
)
17 changes: 11 additions & 6 deletions web/oss/src/services/tracing/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export const fetchAllPreviewTraces = async (
const projectId = ensureProjectId()
const applicationId = ensureAppId(appId)

// New query endpoint expects POST with JSON body
const url = new URL(`${base}/tracing/spans/query`)
// POST /spans/query — always returns flat spans (focus param removed)
const url = new URL(`${base}/spans/query`)
if (projectId) url.searchParams.set("project_id", projectId)
if (applicationId) url.searchParams.set("application_id", applicationId)

Expand All @@ -41,6 +41,9 @@ export const fetchAllPreviewTraces = async (
} catch {
payload.filter = value
}
} else if (key === "focus") {
// `focus` is no longer accepted by POST /spans/query — skip it.
return
} else {
payload[key] = value
}
Expand Down Expand Up @@ -94,7 +97,7 @@ export const fetchAllPreviewTracesWithMeta = async (
const projectId = ensureProjectId()
const applicationId = ensureAppId(appId)

const url = new URL(`${base}/tracing/spans/query`)
const url = new URL(`${base}/spans/query`)
if (projectId) url.searchParams.set("project_id", projectId)
if (applicationId) url.searchParams.set("application_id", applicationId)

Expand All @@ -109,6 +112,8 @@ export const fetchAllPreviewTracesWithMeta = async (
} catch {
payload.filter = value
}
} else if (key === "focus") {
return
} else {
payload[key] = value
}
Expand All @@ -134,7 +139,7 @@ export const fetchPreviewTrace = async (traceId: string) => {
const base = getBaseUrl()
const projectId = ensureProjectId()

const url = new URL(`${base}/tracing/traces/${traceId}`)
const url = new URL(`${base}/traces/${traceId}`)
if (projectId) url.searchParams.set("project_id", projectId)

return fetchJson(url)
Expand All @@ -144,7 +149,7 @@ export const deletePreviewTrace = async (traceId: string) => {
const base = getBaseUrl()
const projectId = ensureProjectId()

const url = new URL(`${base}/tracing/traces/${traceId}`)
const url = new URL(`${base}/traces/${traceId}`)
if (projectId) url.searchParams.set("project_id", projectId)

return fetchJson(url, {method: "DELETE"})
Expand All @@ -167,7 +172,7 @@ export const fetchSessions = async (params: {
const projectId = ensureProjectId()
const applicationId = params.appId ? ensureAppId(params.appId) : undefined

const url = new URL(`${base}/tracing/sessions/query`)
const url = new URL(`${base}/spans/sessions/query`)
if (projectId) url.searchParams.set("project_id", projectId)
if (applicationId) url.searchParams.set("application_id", applicationId)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,15 @@ async function resolveTraceLinkSpanId({

try {
const traceResponse = await fetchPreviewTrace(traceId, projectId)
const traceKey = traceId.replace(/-/g, "")
const traceEntry = traceResponse?.traces?.[traceKey] ?? traceResponse?.traces?.[traceId]
// New API returns {count, trace: {trace_id, spans}} — extract spans
// from the single trace object. Fallback to legacy traces record shape
// for backward compatibility during rollout.
const traceEntry =
traceResponse?.trace ?? (() => {
const traceKey = traceId.replace(/-/g, "")
return (traceResponse as any)?.traces?.[traceKey] ??
(traceResponse as any)?.traces?.[traceId]
})()
Comment on lines +642 to +646
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Remove any from legacy trace fallback parsing.

The fallback currently uses as any in package code. Please replace it with a narrow legacy response type so this path stays type-safe.

Suggested typed rewrite
+type LegacyTraceResponse = {
+    traces?: Record<string, {spans?: Record<string, TraceSpan> | null} | undefined>
+}
+
         const traceEntry =
             traceResponse?.trace ?? (() => {
                 const traceKey = traceId.replace(/-/g, "")
-                return (traceResponse as any)?.traces?.[traceKey] ??
-                    (traceResponse as any)?.traces?.[traceId]
+                const legacy = traceResponse as LegacyTraceResponse | null | undefined
+                return legacy?.traces?.[traceKey] ?? legacy?.traces?.[traceId]
             })()

As per coding guidelines, “Do not use any types in package code; follow strict typing rules.”

Source: Coding guidelines

const rawSpans = traceEntry?.spans ? Object.values(traceEntry.spans) : []
const spans = rawSpans.filter(
(span): span is TraceSpan =>
Expand Down
Loading