From ec8b32a87ab6098ab68c4d9fbff4730c416d95ee Mon Sep 17 00:00:00 2001 From: Umberto Sgueglia Date: Mon, 30 Mar 2026 16:10:39 +0200 Subject: [PATCH 1/3] docs: adding affilaitions api Signed-off-by: Umberto Sgueglia --- .../api/public/v1/affiliations/openapi.yaml | 324 ++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 backend/src/api/public/v1/affiliations/openapi.yaml diff --git a/backend/src/api/public/v1/affiliations/openapi.yaml b/backend/src/api/public/v1/affiliations/openapi.yaml new file mode 100644 index 0000000000..f0744868f8 --- /dev/null +++ b/backend/src/api/public/v1/affiliations/openapi.yaml @@ -0,0 +1,324 @@ +openapi: 3.1.0 +info: + title: LFX Member Organization Affiliations API + version: 1.0.0 + description: > + API for querying developer affiliation periods (work experiences) linked to + their verified GitHub identities in the LFX platform. + +servers: + - url: /api/v1 + description: Production + +security: + - ApiKeyAuth: [] + +paths: + /affiliations: + post: + operationId: getBulkAffiliations + summary: Bulk contributor lookup + description: > + Look up affiliation data for up to 100 GitHub handles in a single + request. Handles that have no matching LFX profile are returned in the + `notFound` array. + tags: + - Affiliations + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - githubHandles + properties: + githubHandles: + type: array + description: List of GitHub login handles to look up (case-insensitive). + minItems: 1 + maxItems: 100 + items: + type: string + minLength: 1 + example: + - torvalds + - gvanrossum + example: + githubHandles: + - torvalds + - gvanrossum + parameters: + - name: page + in: query + description: Page number (1-based). + required: false + schema: + type: integer + minimum: 1 + default: 1 + - name: pageSize + in: query + description: Number of contributors to return per page. + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + responses: + '200': + description: Affiliations resolved successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/BulkAffiliationsResponse' + example: + total: 2 + totalFound: 2 + page: 1 + pageSize: 20 + contributorsInPage: 2 + contributors: + - githubHandle: torvalds + name: Linus Torvalds + emails: + - torvalds@linux-foundation.org + affiliations: + - organization: Linux Foundation + startDate: '2007-01-01' + endDate: null + notFound: [] + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '429': + $ref: '#/components/responses/TooManyRequests' + + /affiliations/{githubHandle}: + get: + operationId: getAffiliationByHandle + summary: Single contributor lookup + description: > + Convenience endpoint for looking up one developer by GitHub handle. + Useful for debugging and ad-hoc queries. + tags: + - Affiliations + parameters: + - name: githubHandle + in: path + required: true + description: GitHub login handle of the developer (case-insensitive). + schema: + type: string + minLength: 1 + example: torvalds + responses: + '200': + description: Developer found. + content: + application/json: + schema: + $ref: '#/components/schemas/Contributor' + example: + githubHandle: torvalds + name: Linus Torvalds + emails: + - torvalds@linux-foundation.org + affiliations: + - organization: Linux Foundation + startDate: '2007-01-01' + endDate: null + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + description: No LFX profile found for the given GitHub handle. + content: + application/json: + schema: + $ref: '#/components/schemas/HttpError' + example: + error: + code: NOT_FOUND + message: "No LFX profile found for GitHub login 'nonexistent-user'." + '429': + $ref: '#/components/responses/TooManyRequests' + +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: Authorization + description: Static API key — pass as `Bearer `. + + schemas: + AffiliationPeriod: + type: object + required: + - organization + - startDate + - endDate + properties: + organization: + type: string + description: Name of the organization. + example: Linux Foundation + startDate: + type: + - string + - 'null' + format: date + description: Start date of the affiliation period (ISO 8601). + example: '2020-01-01' + endDate: + type: + - string + - 'null' + format: date + description: End date of the affiliation period, or null if currently active. + example: null + + Contributor: + type: object + required: + - githubHandle + - name + - emails + - affiliations + properties: + githubHandle: + type: string + description: Verified GitHub login handle. + example: torvalds + name: + type: + - string + - 'null' + description: Display name from the LFX profile. + example: Linus Torvalds + emails: + type: array + description: Verified email addresses linked to the LFX profile. + items: + type: string + format: email + example: + - torvalds@linux-foundation.org + affiliations: + type: array + description: Resolved affiliation periods, ordered from most recent to oldest (null startDate last). + items: + $ref: '#/components/schemas/AffiliationPeriod' + + BulkAffiliationsResponse: + type: object + required: + - total + - totalFound + - page + - pageSize + - contributorsInPage + - contributors + - notFound + properties: + total: + type: integer + description: Total number of handles submitted in the request. + example: 2 + totalFound: + type: integer + description: Number of handles that matched an LFX profile. + example: 2 + page: + type: integer + description: Current page number. + example: 1 + pageSize: + type: integer + description: Maximum contributors per page. + example: 20 + contributorsInPage: + type: integer + description: Number of contributors returned in this page. + example: 2 + contributors: + type: array + items: + $ref: '#/components/schemas/Contributor' + notFound: + type: array + description: Handles from the request that had no matching LFX profile. + items: + type: string + example: [] + + HttpError: + type: object + required: + - error + properties: + error: + type: object + required: + - code + - message + properties: + code: + type: string + description: Machine-readable error code. + example: NOT_FOUND + message: + type: string + description: Human-readable error description. + example: Not found + + responses: + BadRequest: + description: Invalid request body or query parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/HttpError' + example: + error: + code: BAD_REQUEST + message: "githubHandles: Maximum 100 handles per request" + + Unauthorized: + description: Missing or invalid API key. + content: + application/json: + schema: + $ref: '#/components/schemas/HttpError' + example: + error: + code: UNAUTHORIZED + message: Invalid API key + + Forbidden: + description: API key does not have the required scope. + content: + application/json: + schema: + $ref: '#/components/schemas/HttpError' + example: + error: + code: INSUFFICIENT_SCOPE + message: Insufficient scope for this operation + + TooManyRequests: + description: Rate limit exceeded (60 requests per 60 seconds). + content: + application/json: + schema: + $ref: '#/components/schemas/HttpError' + example: + error: + code: RATE_LIMITED + message: Too many requests, please try again later From a50fd8de379fc530f2b8e3b60dbdfb1da4710e9c Mon Sep 17 00:00:00 2001 From: Umberto Sgueglia Date: Mon, 30 Mar 2026 16:15:07 +0200 Subject: [PATCH 2/3] fix: format Signed-off-by: Umberto Sgueglia --- backend/src/api/public/v1/affiliations/openapi.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/public/v1/affiliations/openapi.yaml b/backend/src/api/public/v1/affiliations/openapi.yaml index f0744868f8..cfd72344e6 100644 --- a/backend/src/api/public/v1/affiliations/openapi.yaml +++ b/backend/src/api/public/v1/affiliations/openapi.yaml @@ -288,7 +288,7 @@ components: example: error: code: BAD_REQUEST - message: "githubHandles: Maximum 100 handles per request" + message: 'githubHandles: Maximum 100 handles per request' Unauthorized: description: Missing or invalid API key. From d9e3718234f0fbb793c78bd41da4535a58376e66 Mon Sep 17 00:00:00 2001 From: Umberto Sgueglia Date: Mon, 30 Mar 2026 16:22:09 +0200 Subject: [PATCH 3/3] fix: comments from review Signed-off-by: Umberto Sgueglia --- .../src/api/public/v1/affiliations/openapi.yaml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/backend/src/api/public/v1/affiliations/openapi.yaml b/backend/src/api/public/v1/affiliations/openapi.yaml index cfd72344e6..c3c5473443 100644 --- a/backend/src/api/public/v1/affiliations/openapi.yaml +++ b/backend/src/api/public/v1/affiliations/openapi.yaml @@ -86,7 +86,7 @@ paths: - torvalds@linux-foundation.org affiliations: - organization: Linux Foundation - startDate: '2007-01-01' + startDate: '2007-01-01T00:00:00.000Z' endDate: null notFound: [] '400': @@ -152,9 +152,8 @@ paths: components: securitySchemes: ApiKeyAuth: - type: apiKey - in: header - name: Authorization + type: http + scheme: bearer description: Static API key — pass as `Bearer `. schemas: @@ -173,14 +172,14 @@ components: type: - string - 'null' - format: date - description: Start date of the affiliation period (ISO 8601). - example: '2020-01-01' + format: date-time + description: Start date of the affiliation period (ISO 8601 datetime). + example: '2020-01-01T00:00:00.000Z' endDate: type: - string - 'null' - format: date + format: date-time description: End date of the affiliation period, or null if currently active. example: null