/**
* Scaffolded Sentry OpenAPI client (dependency-free ESM).
*
* Source: openapi_client_scaffold against
* https://raw.githubusercontent.com/getsentry/sentry-api-schema/main/openapi-derefed.json
* Slugs: listorganizations, listorganizationprojects, listorganizationissues
* Auth: bearerSecret sentryAuthToken
*
* Host defaults to sentry.io. Pass `host` for us.sentry.io, de.sentry.io,
* or a self-hosted instance. Secret placeholders only resolve for approved
* hosts.
*/
import { authorizationHeader, getSentryFetch, resolveIntegrationName, type SentryAuthSelection } from './auth.ts'
import { sentryApiBaseUrl } from './host.ts'
export type QueryValue = string | number | boolean | null | undefined
export type QueryInput = Record<string, QueryValue | QueryValue[]>
function authHeaders(auth: SentryAuthSelection = {}): Record<string, string> {
if (resolveIntegrationName(auth)) return {}
return { Authorization: authorizationHeader(auth) }
}
export function buildUrl(
pathTemplate: string,
params: Record<string, unknown> = {},
host?: unknown,
): string {
return sentryApiBaseUrl(host) + pathTemplate.replace(/\{([^}]+)\}/g, (_match, name: string) => {
const value = params[name]
if (value === undefined || value === null) {
throw new Error(`Missing required path parameter: ${name}`)
}
return encodeURIComponent(String(value))
})
}
export function appendQuery(url: string, query: QueryInput = {}): string {
const search = new URLSearchParams()
for (const [key, value] of Object.entries(query)) {
if (value === undefined || value === null || value === '') continue
if (Array.isArray(value)) {
for (const item of value) {
if (item === undefined || item === null || item === '') continue
search.append(key, String(item))
}
continue
}
search.append(key, String(value))
}
const qs = search.toString()
return qs ? `${url}?${qs}` : url
}
function mergeHeaders(
userHeaders: Record<string, string> | undefined,
authHeaders: Record<string, string>,
): Record<string, string> {
const merged: Record<string, string> = { ...(userHeaders ?? {}) }
for (const [key, value] of Object.entries(authHeaders)) {
const lower = key.toLowerCase()
for (const existing of Object.keys(merged)) {
if (existing.toLowerCase() === lower) delete merged[existing]
}
merged[key] = value
}
return merged
}
export type ScaffoldInput = SentryAuthSelection & {
params?: Record<string, unknown>
query?: QueryInput
headers?: Record<string, string>
body?: unknown
host?: string
}
function requestAuth(input: SentryAuthSelection = {}): SentryAuthSelection {
return { secretName: input.secretName, integration: input.integration }
}
async function sentryFetch(url: string, init: RequestInit, auth: SentryAuthSelection = {}): Promise<Response> {
const fetchFn = await getSentryFetch(auth)
return fetchFn(url, init)
}
/** GET /api/0/organizations/ — List Your Organizations */
export async function listorganizations(input: ScaffoldInput = {}): Promise<Response> {
const params = input.params ?? {}
const url = appendQuery(buildUrl('/api/0/organizations/', params, input.host), input.query)
const auth = requestAuth(input)
return sentryFetch(url, { method: 'GET', headers: mergeHeaders(input.headers, authHeaders(auth)) }, auth)
}
/** GET /api/0/organizations/{organization_id_or_slug}/projects/ */
export async function listorganizationprojects(input: ScaffoldInput = {}): Promise<Response> {
const params = input.params ?? {}
if (params.organization_id_or_slug === undefined || params.organization_id_or_slug === null) {
throw new Error('Missing required path parameter: organization_id_or_slug')
}
const url = appendQuery(
buildUrl('/api/0/organizations/{organization_id_or_slug}/projects/', params, input.host),
input.query,
)
const auth = requestAuth(input)
return sentryFetch(url, { method: 'GET', headers: mergeHeaders(input.headers, authHeaders(auth)) }, auth)
}
/** GET /api/0/organizations/{organization_id_or_slug}/issues/ */
export async function listorganizationissues(input: ScaffoldInput = {}): Promise<Response> {
const params = input.params ?? {}
if (params.organization_id_or_slug === undefined || params.organization_id_or_slug === null) {
throw new Error('Missing required path parameter: organization_id_or_slug')
}
const url = appendQuery(
buildUrl('/api/0/organizations/{organization_id_or_slug}/issues/', params, input.host),
input.query,
)
const auth = requestAuth(input)
return sentryFetch(url, { method: 'GET', headers: mergeHeaders(input.headers, authHeaders(auth)) }, auth)
}
/**
* Minimal fetch for endpoints absent from the public OpenAPI inventory
* (e.g. /api/0/issues/{id}/events/latest/). Path is relative to /api/0.
*/
export async function rawSentryRequest(
path: string,
options: { query?: QueryInput; init?: RequestInit; host?: string } & SentryAuthSelection = {},
): Promise<Response> {
const normalizedPath = path.startsWith('/') ? path : '/' + path
const url = appendQuery(`${sentryApiBaseUrl(options.host)}/api/0${normalizedPath}`, options.query)
const auth = requestAuth(options)
const headers = mergeHeaders(
{
accept: 'application/json',
...(options.init?.headers as Record<string, string> | undefined),
},
authHeaders(auth),
)
return sentryFetch(url, { ...options.init, headers }, auth)
}