← Public packages
@kentcdodds/sentry
Inspect Sentry organizations, projects, issues, and event summaries.
src/openapi-client.ts
123 lines · 4.5 KB · TypeScript/**
* 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
*
* Unscoped {{secret:sentryAuthToken}} resolves for this user-scoped secret
* (verified HTTP 200); scoped form also works.
*/
export type QueryValue = string | number | boolean | null | undefined
export type QueryInput = Record<string, QueryValue | QueryValue[]>
const API_BASE_URL = 'https://sentry.io'
const SENTRY_AUTH_HEADER = 'Bearer {{secret:sentryAuthToken}}'
export function buildUrl(pathTemplate: string, params: Record<string, unknown> = {}): string {
return API_BASE_URL + 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
}
function authHeaders(): Record<string, string> {
return { Authorization: SENTRY_AUTH_HEADER }
}
export type ScaffoldInput = {
params?: Record<string, unknown>
query?: QueryInput
headers?: Record<string, string>
body?: unknown
}
/** 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.query)
return fetch(url, { method: 'GET', headers: mergeHeaders(input.headers, authHeaders()) })
}
/** 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.query,
)
return fetch(url, { method: 'GET', headers: mergeHeaders(input.headers, authHeaders()) })
}
/** 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.query,
)
return fetch(url, { method: 'GET', headers: mergeHeaders(input.headers, authHeaders()) })
}
/**
* 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 } = {},
): Promise<Response> {
const normalizedPath = path.startsWith('/') ? path : '/' + path
const url = appendQuery(`${API_BASE_URL}/api/0${normalizedPath}`, options.query)
const headers = mergeHeaders(
{
accept: 'application/json',
...(options.init?.headers as Record<string, string> | undefined),
},
authHeaders(),
)
return fetch(url, { ...options.init, headers })
}