-
Notifications
You must be signed in to change notification settings - Fork 8
refactor(api): new endpoints for slackbot #293
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
76b8d33
3e9fb58
552e3f7
7e255ad
d394f19
4a71931
d57099c
2c94902
6c86da2
73b93c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| import { beforeEach, describe, expect, it, vi } from "vitest"; | ||
|
|
||
| const { apiReferenceMock, generateMock } = vi.hoisted(() => ({ | ||
| apiReferenceMock: vi.fn(() => () => new Response("openapi-ui")), | ||
| generateMock: vi.fn(async () => ({ | ||
| paths: { | ||
| "/v1/ping": { | ||
| get: {}, | ||
| post: {}, | ||
| }, | ||
| }, | ||
| components: {}, | ||
| })), | ||
| })); | ||
|
|
||
| vi.mock("@acme/env", () => ({ | ||
| env: { | ||
| NEXT_PUBLIC_API_URL: "https://api.example.com", | ||
| }, | ||
| })); | ||
|
|
||
| vi.mock("@acme/api", () => ({ | ||
| router: {}, | ||
| })); | ||
|
|
||
| vi.mock("@scalar/nextjs-api-reference", () => ({ | ||
| ApiReference: apiReferenceMock, | ||
| })); | ||
|
|
||
| vi.mock("@orpc/openapi", () => ({ | ||
| OpenAPIGenerator: vi.fn(() => ({ | ||
| generate: generateMock, | ||
| })), | ||
| })); | ||
|
|
||
| vi.mock("@orpc/zod", () => ({ | ||
| ZodToJsonSchemaConverter: vi.fn(), | ||
| })); | ||
|
|
||
| import { GET as getDocs } from "../src/app/docs/route"; | ||
| import { GET as getOpenApiJson } from "../src/app/docs/openapi.json/route"; | ||
|
|
||
| describe("docs routes", () => { | ||
| beforeEach(() => { | ||
| apiReferenceMock.mockClear(); | ||
| }); | ||
|
|
||
| it("renders the API reference page with the configured base URL", async () => { | ||
| const response = await getDocs(); | ||
|
|
||
| expect(response.status).toBe(200); | ||
| await expect(response.text()).resolves.toBe("openapi-ui"); | ||
| expect(apiReferenceMock).toHaveBeenCalledWith( | ||
| expect.objectContaining({ | ||
| url: "/docs/openapi.json", | ||
| baseServerURL: "https://api.example.com", | ||
| pageTitle: "F3 Nation API Reference", | ||
| favicon: "/favicon.ico", | ||
| }), | ||
| ); | ||
| }); | ||
|
|
||
| it("returns an OpenAPI JSON document with client headers injected", async () => { | ||
| const response = await getOpenApiJson( | ||
| new Request("https://api.example.com/docs/openapi.json"), | ||
| ); | ||
|
|
||
| expect(response.status).toBe(200); | ||
|
|
||
| const spec = (await response.json()) as { | ||
| components?: { | ||
| parameters?: Record<string, { name: string; in: string }>; | ||
| }; | ||
| paths?: Record< | ||
| string, | ||
| { | ||
| get?: { parameters?: { $ref?: string }[] }; | ||
| post?: { parameters?: { $ref?: string }[] }; | ||
| } | ||
| >; | ||
| }; | ||
|
|
||
| expect(spec.components?.parameters?.ClientHeader).toMatchObject({ | ||
| name: "client", | ||
| in: "header", | ||
| }); | ||
| expect(spec.paths?.["/v1/ping"]?.get?.parameters?.[0]).toMatchObject({ | ||
| $ref: "#/components/parameters/ClientHeader", | ||
| }); | ||
| expect(spec.paths?.["/v1/ping"]?.post?.parameters?.[0]).toMatchObject({ | ||
| $ref: "#/components/parameters/ClientHeader", | ||
| }); | ||
|
|
||
| const generateCalls = generateMock.mock.calls as unknown[][]; | ||
| const generateOptions = generateCalls[0]?.[1] as | ||
| | { filter?: (args: { path: string[] }) => boolean } | ||
| | undefined; | ||
|
|
||
| expect(generateOptions?.filter).toBeTypeOf("function"); | ||
| expect(generateOptions?.filter?.({ path: ["slack"] })).toBe(false); | ||
| expect(generateOptions?.filter?.({ path: ["ping"] })).toBe(true); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -54,6 +54,7 @@ export async function GET(request: Request) { | |||||
| }); | ||||||
|
|
||||||
| const spec = (await generator.generate(router, { | ||||||
| filter: ({ path }) => path[0] !== "slack", | ||||||
| info: { | ||||||
| title: "F3 Nation API", | ||||||
| version: packageJson.version, | ||||||
|
|
@@ -106,6 +107,7 @@ API keys inherit the roles and permissions of their owner. Access levels include | |||||
| - **Editor**: Can view and modify data within assigned organizations | ||||||
| - **Admin**: Same permissions as Editor, plus the ability to add/remove other Admins and Editors | ||||||
|
|
||||||
|
|
||||||
| As of February 1, 2026, regional admins can only create read-only API keys. If you want an API Key edit access to your region, you will need to contact an F3 Nation admin. Right now, regional edit access is highly restrcited. This is because the API is still new and there are almost certainly gaps in security - meaning that a region has the potential to mess up data for other regions. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix typo in public API policy text. “restrcited” should be “restricted”. Suggested fix-Right now, regional edit access is highly restrcited.
+Right now, regional edit access is highly restricted.📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
|
|
||||||
| ## Best Practices | ||||||
|
|
@@ -119,6 +121,7 @@ As of February 1, 2026, regional admins can only create read-only API keys. If y | |||||
| - **401 Unauthorized**: Missing, invalid, revoked, or expired API key | ||||||
| - **403 Forbidden**: Valid API key but insufficient permissions for the requested resource | ||||||
| - **429 Too Many Requests**: Rate limit exceeded (200 requests per 60 seconds)`, | ||||||
|
|
||||||
| contact: { | ||||||
| name: "F3 Nation", | ||||||
| url: "https://f3nation.com", | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: F3-Nation/f3-nation
Length of output: 190
🏁 Script executed:
Repository: F3-Nation/f3-nation
Length of output: 15106
🏁 Script executed:
Repository: F3-Nation/f3-nation
Length of output: 3153
🏁 Script executed:
Repository: F3-Nation/f3-nation
Length of output: 4726
🌐 Web query:
@orpc/openapi OpenAPIGenerator filter ({ path }) signature path array includes prefix💡 Result:
In the
@orpc/openapipackage, the OpenAPIGeneratorfilteroption allows you to include or exclude procedures from the generated OpenAPI specification [1][2]. Thefilterfunction receives an object containingpathas one of its arguments, which is areadonly string[]array representing the path to the procedure within your router [3][4]. You can check if the path includes a specific prefix by inspecting the elements of this array [1][2]. For example, if you want to exclude any procedures under an "internal" path, you can use the following approach [1][2]: const spec = await generator.generate(router, { filter: ({ path }) => path!== 'internal', }) In this example,path[0] === 'internal'checks if the first segment of the path is 'internal' [1][2]. Becausepathis an array of strings, you can use standard JavaScript array methods likeincludes()if you need to check for a prefix that might exist at any level of the path hierarchy, or simply index into the array if the prefix is always at a specific depth [1][2]. Thefilterfunction should returntrueto include the procedure orfalseto exclude it [1][3][2].Citations:
Fix OpenAPI Slack filtering to handle the
/v1prefix (and correct docs typo).apps/api/src/app/docs/openapi.json/route.tsbuilds the spec fromrouterimported from@acme/api, whosepackages/api/src/index.tsprefixes all routes withAPI_PREFIX_V1(/v1). That means Slack endpoints are typically underpath[1], sopath[0] !== "slack"won’t filter them out.restrcited→restrictedin the OpenAPIinfo.description.Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents