diff --git a/api.yaml b/api.yaml index aeda714..eee61a9 100644 --- a/api.yaml +++ b/api.yaml @@ -1530,7 +1530,241 @@ paths: $ref: '#/components/responses/ApiKeyInsufficientPermissions' '429': $ref: '#/components/responses/TooManyRequests' - + + # ---------------------------------------------------------------------- + # User Access Key (UAK) endpoints (TES-27473) + # See https://tricentis.atlassian.net/wiki/spaces/TESTIM/pages/2445803704 + # ---------------------------------------------------------------------- + /projects/{projectId}/users: + get: + summary: List Users in Project + description: Returns the list of users in the project. Authenticated with a User Access Key. + tags: + - Project Users + security: + - userAccessKeyAuth: [] + parameters: + - in: path + name: projectId + required: true + description: Project ID + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + projectUsers: + type: array + items: + $ref: '#/components/schemas/projectUser' + metaData: + $ref: '#/components/schemas/uakMetaData' + required: + - projectUsers + - metaData + '401': + $ref: '#/components/responses/InvalidApiKey' + '403': + $ref: '#/components/responses/Forbidden' + '429': + $ref: '#/components/responses/TooManyRequests' + /projects/{projectId}/users/activity: + get: + summary: Get User Activity in Project + description: | + Returns per-user productivity for the project, computed from the master branch only. + The `current` window covers the last `previousPeriodDays` days; `previous` covers the + equally-sized window immediately before it. Dates are ISO `YYYY-MM-DD`. + tags: + - Project Users + security: + - userAccessKeyAuth: [] + parameters: + - in: path + name: projectId + required: true + description: Project ID + schema: + type: string + - in: query + name: previousPeriodDays + required: false + description: Width in days of both the current and previous activity windows. + schema: + type: integer + minimum: 1 + maximum: 365 + default: 30 + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + activity: + type: object + additionalProperties: + $ref: '#/components/schemas/userActivityEntry' + example: + john@test.com: + name: 'John Doe' + current: { startDate: '2026-05-25', endDate: '2026-06-24', createdTests: 5, updatedTests: 3 } + previous: { startDate: '2026-04-25', endDate: '2026-05-25', createdTests: 2, updatedTests: 1 } + metaData: + $ref: '#/components/schemas/uakMetaData' + required: + - activity + - metaData + '400': + $ref: '#/components/responses/InvalidOrMissingProperty' + '401': + $ref: '#/components/responses/InvalidApiKey' + '403': + $ref: '#/components/responses/Forbidden' + '429': + $ref: '#/components/responses/TooManyRequests' + /projects/{projectId}/users/invite: + post: + summary: Invite Users to Project + description: Invites one or more users to the project by email. Empty arrays are rejected. + tags: + - Project Users + security: + - userAccessKeyAuth: [] + parameters: + - in: path + name: projectId + required: true + description: Project ID + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EmailListBody' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/SuccessResponse' + '400': + $ref: '#/components/responses/InvalidOrMissingProperty' + '401': + $ref: '#/components/responses/InvalidApiKey' + '403': + $ref: '#/components/responses/Forbidden' + '429': + $ref: '#/components/responses/TooManyRequests' + /projects/{projectId}/users/remove: + delete: + summary: Remove Users from Project + description: | + Removes one or more users from the project by email. Emails that don't match an existing + user, or that match a user not currently in the project, are silently skipped. + Authorization guards (cannot delete company owners, sole project owners, etc.) still apply. + tags: + - Project Users + security: + - userAccessKeyAuth: [] + parameters: + - in: path + name: projectId + required: true + description: Project ID + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EmailListBody' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/SuccessResponse' + '400': + $ref: '#/components/responses/InvalidOrMissingProperty' + '401': + $ref: '#/components/responses/InvalidApiKey' + '403': + $ref: '#/components/responses/Forbidden' + '429': + $ref: '#/components/responses/TooManyRequests' + /companies/{companyId}/projects: + post: + summary: Create a Project + description: Creates a new project in the given company. Caller must be a company owner. + tags: + - Company Projects + security: + - userAccessKeyAuth: [] + parameters: + - in: path + name: companyId + required: true + description: Company ID + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - projectName + - type + properties: + projectName: + type: string + example: 'My new project' + type: + type: string + enum: ['desktopWeb', 'mobileWeb', 'android', 'ios', 'sfdc'] + responses: + '201': + description: Project created + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + id: + type: string + description: The ID of the newly created project. + metaData: + $ref: '#/components/schemas/uakMetaData' + required: + - success + - id + - metaData + '400': + $ref: '#/components/responses/InvalidOrMissingProperty' + '401': + $ref: '#/components/responses/InvalidApiKey' + '403': + $ref: '#/components/responses/Forbidden' + '429': + $ref: '#/components/responses/TooManyRequests' + tags: - name: Branches description: List, create, delete and merge branches @@ -1544,6 +1778,10 @@ tags: description: Execute labels - name: Executions description: Get execution results + - name: Project Users + description: Manage and inspect users within a project (User Access Key auth) + - name: Company Projects + description: Manage projects within a company (User Access Key auth) security: - apiKeyAuth: [] @@ -1661,6 +1899,15 @@ components: application/json: schema: $ref: '#/components/schemas/Error' + Forbidden: + description: | + Caller is not authorized for this resource. The response message includes a + `TEID-` error id that can be used to look up the matching internal log + entry (`apiLogger.warn('UAK authorization rejected', { errorId, reason, ... })`). + content: + application/json: + schema: + $ref: '#/components/schemas/Error' schemas: # requestId attached to all requests and responses requestId: @@ -1703,6 +1950,98 @@ components: $ref: '#/components/schemas/requestId' required: - requestId + # MetaData attached to UAK responses. monthly counters appear only on project-scoped endpoints. + uakMetaData: + type: object + properties: + requestId: + $ref: '#/components/schemas/requestId' + projectId: + type: string + companyId: + type: string + currentRequestCount: + type: integer + description: Project's monthly request count so far (only present on project-scoped endpoints). + monthlyRequestLimit: + type: integer + description: Project's monthly request limit (only present on project-scoped endpoints). + required: + - requestId + projectUser: + type: object + properties: + name: + type: string + email: + type: string + format: email + role: + type: string + example: 'editor' + required: + - email + - role + activityWindow: + type: object + properties: + startDate: + type: string + format: date + example: '2026-05-25' + endDate: + type: string + format: date + example: '2026-06-24' + createdTests: + type: integer + minimum: 0 + updatedTests: + type: integer + minimum: 0 + required: + - startDate + - endDate + - createdTests + - updatedTests + userActivityEntry: + type: object + properties: + name: + type: string + current: + $ref: '#/components/schemas/activityWindow' + previous: + $ref: '#/components/schemas/activityWindow' + required: + - name + - current + - previous + EmailListBody: + type: object + required: + - emails + properties: + emails: + type: array + minItems: 1 + items: + type: string + format: email + example: + - 'user1@test.com' + - 'user2@test.com' + SuccessResponse: + type: object + properties: + success: + type: boolean + example: true + metaData: + $ref: '#/components/schemas/uakMetaData' + required: + - success + - metaData # Date resource was created at created_at: type: string @@ -1740,3 +2079,12 @@ components: type: http scheme: bearer bearerFormat: PAK-[RANDOM_KEY] + userAccessKeyAuth: + type: apiKey + in: header + name: x-user-access-key + description: | + User Access Key (UAK) of the form `UAK--`. The userId is + base64-encoded so that any userId format (alphanumeric, Salesforce, CI) can be carried + unambiguously. Secrets are hashed with PBKDF2/HMAC-SHA256 server-side and compared in + constant time. \ No newline at end of file