diff --git a/spec/openapi.infra.yaml b/spec/openapi.infra.yaml index f49e8f789..14ecedc89 100644 --- a/spec/openapi.infra.yaml +++ b/spec/openapi.infra.yaml @@ -102,42 +102,42 @@ components: type: string responses: - '400': + "400": description: Bad request content: application/json: schema: - $ref: '#/components/schemas/Error' - '401': + $ref: "#/components/schemas/Error" + "401": description: Authentication error content: application/json: schema: - $ref: '#/components/schemas/Error' - '403': + $ref: "#/components/schemas/Error" + "403": description: Forbidden content: application/json: schema: - $ref: '#/components/schemas/Error' - '404': + $ref: "#/components/schemas/Error" + "404": description: Not found content: application/json: schema: - $ref: '#/components/schemas/Error' - '409': + $ref: "#/components/schemas/Error" + "409": description: Conflict content: application/json: schema: - $ref: '#/components/schemas/Error' - '500': + $ref: "#/components/schemas/Error" + "500": description: Server error content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: "#/components/schemas/Error" schemas: Team: @@ -284,7 +284,7 @@ components: type: string description: Log message content level: - $ref: '#/components/schemas/LogLevel' + $ref: "#/components/schemas/LogLevel" fields: type: object additionalProperties: @@ -299,12 +299,12 @@ components: description: Logs of the sandbox type: array items: - $ref: '#/components/schemas/SandboxLog' + $ref: "#/components/schemas/SandboxLog" logEntries: description: Structured logs of the sandbox type: array items: - $ref: '#/components/schemas/SandboxLogEntry' + $ref: "#/components/schemas/SandboxLogEntry" SandboxMetric: description: Metric entry with timestamp and line @@ -373,7 +373,7 @@ components: deprecated: true description: Identifier of the client envdVersion: - $ref: '#/components/schemas/EnvdVersion' + $ref: "#/components/schemas/EnvdVersion" envdAccessToken: type: string description: Access token used for envd communication @@ -421,7 +421,7 @@ components: format: date-time description: Time when the sandbox will expire envdVersion: - $ref: '#/components/schemas/EnvdVersion' + $ref: "#/components/schemas/EnvdVersion" envdAccessToken: type: string description: Access token used for envd communication @@ -430,15 +430,15 @@ components: nullable: true description: Base domain where the sandbox traffic is accessible cpuCount: - $ref: '#/components/schemas/CPUCount' + $ref: "#/components/schemas/CPUCount" memoryMB: - $ref: '#/components/schemas/MemoryMB' + $ref: "#/components/schemas/MemoryMB" diskSizeMB: - $ref: '#/components/schemas/DiskSizeMB' + $ref: "#/components/schemas/DiskSizeMB" metadata: - $ref: '#/components/schemas/SandboxMetadata' + $ref: "#/components/schemas/SandboxMetadata" state: - $ref: '#/components/schemas/SandboxState' + $ref: "#/components/schemas/SandboxState" ListedSandbox: required: @@ -475,17 +475,17 @@ components: format: date-time description: Time when the sandbox will expire cpuCount: - $ref: '#/components/schemas/CPUCount' + $ref: "#/components/schemas/CPUCount" memoryMB: - $ref: '#/components/schemas/MemoryMB' + $ref: "#/components/schemas/MemoryMB" diskSizeMB: - $ref: '#/components/schemas/DiskSizeMB' + $ref: "#/components/schemas/DiskSizeMB" metadata: - $ref: '#/components/schemas/SandboxMetadata' + $ref: "#/components/schemas/SandboxMetadata" state: - $ref: '#/components/schemas/SandboxState' + $ref: "#/components/schemas/SandboxState" envdVersion: - $ref: '#/components/schemas/EnvdVersion' + $ref: "#/components/schemas/EnvdVersion" SandboxesWithMetrics: required: @@ -493,7 +493,7 @@ components: properties: sandboxes: additionalProperties: - $ref: '#/components/schemas/SandboxMetric' + $ref: "#/components/schemas/SandboxMetric" NewSandbox: required: @@ -521,13 +521,13 @@ components: Allow sandbox to access the internet. When set to false, it behaves the same as specifying denyOut to 0.0.0.0/0 in the network config. network: - $ref: '#/components/schemas/SandboxNetworkConfig' + $ref: "#/components/schemas/SandboxNetworkConfig" metadata: - $ref: '#/components/schemas/SandboxMetadata' + $ref: "#/components/schemas/SandboxMetadata" envVars: - $ref: '#/components/schemas/EnvVars' + $ref: "#/components/schemas/EnvVars" mcp: - $ref: '#/components/schemas/Mcp' + $ref: "#/components/schemas/Mcp" ResumedSandbox: properties: @@ -637,11 +637,11 @@ components: type: string description: Identifier of the last successful build for given template cpuCount: - $ref: '#/components/schemas/CPUCount' + $ref: "#/components/schemas/CPUCount" memoryMB: - $ref: '#/components/schemas/MemoryMB' + $ref: "#/components/schemas/MemoryMB" diskSizeMB: - $ref: '#/components/schemas/DiskSizeMB' + $ref: "#/components/schemas/DiskSizeMB" public: type: boolean description: Whether the template is public or only accessible by the team @@ -666,7 +666,7 @@ components: description: Time when the template was last updated createdBy: allOf: - - $ref: '#/components/schemas/TeamUser' + - $ref: "#/components/schemas/TeamUser" nullable: true lastSpawnedAt: type: string @@ -682,9 +682,9 @@ components: format: int32 description: Number of times the template was built envdVersion: - $ref: '#/components/schemas/EnvdVersion' + $ref: "#/components/schemas/EnvdVersion" buildStatus: - $ref: '#/components/schemas/TemplateBuildStatus' + $ref: "#/components/schemas/TemplateBuildStatus" TemplateRequestResponseV3: required: @@ -745,11 +745,11 @@ components: type: string description: Identifier of the last successful build for given template cpuCount: - $ref: '#/components/schemas/CPUCount' + $ref: "#/components/schemas/CPUCount" memoryMB: - $ref: '#/components/schemas/MemoryMB' + $ref: "#/components/schemas/MemoryMB" diskSizeMB: - $ref: '#/components/schemas/DiskSizeMB' + $ref: "#/components/schemas/DiskSizeMB" public: type: boolean description: Whether the template is public or only accessible by the team @@ -768,7 +768,7 @@ components: description: Time when the template was last updated createdBy: allOf: - - $ref: '#/components/schemas/TeamUser' + - $ref: "#/components/schemas/TeamUser" nullable: true lastSpawnedAt: type: string @@ -784,7 +784,7 @@ components: format: int32 description: Number of times the template was built envdVersion: - $ref: '#/components/schemas/EnvdVersion' + $ref: "#/components/schemas/EnvdVersion" TemplateBuild: required: @@ -800,7 +800,7 @@ components: format: uuid description: Identifier of the build status: - $ref: '#/components/schemas/TemplateBuildStatus' + $ref: "#/components/schemas/TemplateBuildStatus" createdAt: type: string format: date-time @@ -814,13 +814,13 @@ components: format: date-time description: Time when the build was finished cpuCount: - $ref: '#/components/schemas/CPUCount' + $ref: "#/components/schemas/CPUCount" memoryMB: - $ref: '#/components/schemas/MemoryMB' + $ref: "#/components/schemas/MemoryMB" diskSizeMB: - $ref: '#/components/schemas/DiskSizeMB' + $ref: "#/components/schemas/DiskSizeMB" envdVersion: - $ref: '#/components/schemas/EnvdVersion' + $ref: "#/components/schemas/EnvdVersion" TemplateWithBuilds: required: @@ -872,7 +872,7 @@ components: type: array description: List of builds for the template items: - $ref: '#/components/schemas/TemplateBuild' + $ref: "#/components/schemas/TemplateBuild" TemplateAliasResponse: required: @@ -906,9 +906,9 @@ components: description: Ready check command to execute in the template after the build type: string cpuCount: - $ref: '#/components/schemas/CPUCount' + $ref: "#/components/schemas/CPUCount" memoryMB: - $ref: '#/components/schemas/MemoryMB' + $ref: "#/components/schemas/MemoryMB" TemplateStep: description: Step in the template build process @@ -951,9 +951,9 @@ components: type: string description: Identifier of the team cpuCount: - $ref: '#/components/schemas/CPUCount' + $ref: "#/components/schemas/CPUCount" memoryMB: - $ref: '#/components/schemas/MemoryMB' + $ref: "#/components/schemas/MemoryMB" TemplateBuildRequestV2: required: @@ -967,21 +967,21 @@ components: type: string description: Identifier of the team cpuCount: - $ref: '#/components/schemas/CPUCount' + $ref: "#/components/schemas/CPUCount" memoryMB: - $ref: '#/components/schemas/MemoryMB' + $ref: "#/components/schemas/MemoryMB" FromImageRegistry: oneOf: - - $ref: '#/components/schemas/AWSRegistry' - - $ref: '#/components/schemas/GCPRegistry' - - $ref: '#/components/schemas/GeneralRegistry' + - $ref: "#/components/schemas/AWSRegistry" + - $ref: "#/components/schemas/GCPRegistry" + - $ref: "#/components/schemas/GeneralRegistry" discriminator: propertyName: type mapping: - aws: '#/components/schemas/AWSRegistry' - gcp: '#/components/schemas/GCPRegistry' - registry: '#/components/schemas/GeneralRegistry' + aws: "#/components/schemas/AWSRegistry" + gcp: "#/components/schemas/GCPRegistry" + registry: "#/components/schemas/GeneralRegistry" AWSRegistry: type: object @@ -1047,7 +1047,7 @@ components: type: string description: Template to use as a base for the template build fromImageRegistry: - $ref: '#/components/schemas/FromImageRegistry' + $ref: "#/components/schemas/FromImageRegistry" force: default: false type: boolean @@ -1057,7 +1057,7 @@ components: description: List of steps to execute in the template build type: array items: - $ref: '#/components/schemas/TemplateStep' + $ref: "#/components/schemas/TemplateStep" startCmd: description: Start command to execute in the template after the build type: string @@ -1099,7 +1099,7 @@ components: type: string description: Log message content level: - $ref: '#/components/schemas/LogLevel' + $ref: "#/components/schemas/LogLevel" step: type: string description: Step in the build process related to the log entry @@ -1119,7 +1119,7 @@ components: description: Log entries related to the status reason type: array items: - $ref: '#/components/schemas/BuildLogEntry' + $ref: "#/components/schemas/BuildLogEntry" TemplateBuildStatus: type: string @@ -1149,7 +1149,7 @@ components: description: Build logs structured type: array items: - $ref: '#/components/schemas/BuildLogEntry' + $ref: "#/components/schemas/BuildLogEntry" templateID: type: string description: Identifier of the template @@ -1157,9 +1157,9 @@ components: type: string description: Identifier of the build status: - $ref: '#/components/schemas/TemplateBuildStatus' + $ref: "#/components/schemas/TemplateBuildStatus" reason: - $ref: '#/components/schemas/BuildStatusReason' + $ref: "#/components/schemas/BuildStatusReason" TemplateBuildLogsResponse: required: @@ -1170,7 +1170,7 @@ components: description: Build logs structured type: array items: - $ref: '#/components/schemas/BuildLogEntry' + $ref: "#/components/schemas/BuildLogEntry" LogsDirection: type: string @@ -1215,7 +1215,7 @@ components: format: uuid description: Identifier of the cluster status: - $ref: '#/components/schemas/NodeStatus' + $ref: "#/components/schemas/NodeStatus" DiskMetrics: required: @@ -1282,7 +1282,7 @@ components: type: array description: Detailed metrics for each disk/mount point items: - $ref: '#/components/schemas/DiskMetrics' + $ref: "#/components/schemas/DiskMetrics" MachineInfo: required: - cpuFamily @@ -1339,15 +1339,15 @@ components: type: string description: Identifier of the cluster machineInfo: - $ref: '#/components/schemas/MachineInfo' + $ref: "#/components/schemas/MachineInfo" status: - $ref: '#/components/schemas/NodeStatus' + $ref: "#/components/schemas/NodeStatus" sandboxCount: type: integer format: uint32 description: Number of sandboxes running on the node metrics: - $ref: '#/components/schemas/NodeMetrics' + $ref: "#/components/schemas/NodeMetrics" createSuccesses: type: integer format: uint64 @@ -1397,16 +1397,16 @@ components: deprecated: true description: Identifier of the nomad node machineInfo: - $ref: '#/components/schemas/MachineInfo' + $ref: "#/components/schemas/MachineInfo" status: - $ref: '#/components/schemas/NodeStatus' + $ref: "#/components/schemas/NodeStatus" sandboxes: type: array description: List of sandboxes running on the node items: - $ref: '#/components/schemas/ListedSandbox' + $ref: "#/components/schemas/ListedSandbox" metrics: - $ref: '#/components/schemas/NodeMetrics' + $ref: "#/components/schemas/NodeMetrics" cachedBuilds: type: array description: List of cached builds id on the node @@ -1440,7 +1440,7 @@ components: type: string description: The fully created access token mask: - $ref: '#/components/schemas/IdentifierMaskingDetails' + $ref: "#/components/schemas/IdentifierMaskingDetails" createdAt: type: string format: date-time @@ -1469,14 +1469,14 @@ components: type: string description: Name of the API key mask: - $ref: '#/components/schemas/IdentifierMaskingDetails' + $ref: "#/components/schemas/IdentifierMaskingDetails" createdAt: type: string format: date-time description: Timestamp of API key creation createdBy: allOf: - - $ref: '#/components/schemas/TeamUser' + - $ref: "#/components/schemas/TeamUser" nullable: true lastUsed: type: string @@ -1500,7 +1500,7 @@ components: type: string description: Raw value of the API key mask: - $ref: '#/components/schemas/IdentifierMaskingDetails' + $ref: "#/components/schemas/IdentifierMaskingDetails" name: type: string description: Name of the API key @@ -1510,7 +1510,7 @@ components: description: Timestamp of API key creation createdBy: allOf: - - $ref: '#/components/schemas/TeamUser' + - $ref: "#/components/schemas/TeamUser" nullable: true lastUsed: type: string @@ -1623,10 +1623,10 @@ paths: get: description: Health check responses: - '200': + "200": description: Request was successful - '401': - $ref: '#/components/responses/401' + "401": + $ref: "#/components/responses/401" /teams: get: @@ -1636,7 +1636,7 @@ paths: - AccessTokenAuth: [] - Supabase1TokenAuth: [] responses: - '200': + "200": description: Successfully returned all teams content: application/json: @@ -1644,11 +1644,11 @@ paths: type: array items: allOf: - - $ref: '#/components/schemas/Team' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + - $ref: "#/components/schemas/Team" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /teams/{teamID}/metrics: get: @@ -1659,7 +1659,7 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/teamID' + - $ref: "#/components/parameters/teamID" - in: query name: start schema: @@ -1675,22 +1675,22 @@ paths: minimum: 0 description: Unix timestamp for the end of the interval, in seconds, for which the metrics responses: - '200': + "200": description: Successfully returned the team metrics content: application/json: schema: type: array items: - $ref: '#/components/schemas/TeamMetric' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '403': - $ref: '#/components/responses/403' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/TeamMetric" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "500": + $ref: "#/components/responses/500" /teams/{teamID}/metrics/max: get: @@ -1701,7 +1701,7 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/teamID' + - $ref: "#/components/parameters/teamID" - in: query name: start schema: @@ -1724,20 +1724,20 @@ paths: enum: [concurrent_sandboxes, sandbox_start_rate] description: Metric to retrieve the maximum value for responses: - '200': + "200": description: Successfully returned the team metrics content: application/json: schema: - $ref: '#/components/schemas/MaxTeamMetric' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '403': - $ref: '#/components/responses/403' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/MaxTeamMetric" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "500": + $ref: "#/components/responses/500" /sandboxes: get: @@ -1755,7 +1755,7 @@ paths: schema: type: string responses: - '200': + "200": description: Successfully returned all running sandboxes content: application/json: @@ -1763,13 +1763,13 @@ paths: type: array items: allOf: - - $ref: '#/components/schemas/ListedSandbox' - '401': - $ref: '#/components/responses/401' - '400': - $ref: '#/components/responses/400' - '500': - $ref: '#/components/responses/500' + - $ref: "#/components/schemas/ListedSandbox" + "401": + $ref: "#/components/responses/401" + "400": + $ref: "#/components/responses/400" + "500": + $ref: "#/components/responses/500" post: description: Create a sandbox from the template tags: [sandboxes] @@ -1782,20 +1782,20 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/NewSandbox' + $ref: "#/components/schemas/NewSandbox" responses: - '201': + "201": description: The sandbox was created successfully content: application/json: schema: - $ref: '#/components/schemas/Sandbox' - '401': - $ref: '#/components/responses/401' - '400': - $ref: '#/components/responses/400' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/Sandbox" + "401": + $ref: "#/components/responses/401" + "400": + $ref: "#/components/responses/400" + "500": + $ref: "#/components/responses/500" /v2/sandboxes: get: @@ -1819,13 +1819,13 @@ paths: schema: type: array items: - $ref: '#/components/schemas/SandboxState' + $ref: "#/components/schemas/SandboxState" style: form explode: false - - $ref: '#/components/parameters/paginationNextToken' - - $ref: '#/components/parameters/paginationLimit' + - $ref: "#/components/parameters/paginationNextToken" + - $ref: "#/components/parameters/paginationLimit" responses: - '200': + "200": description: Successfully returned all running sandboxes content: application/json: @@ -1833,13 +1833,13 @@ paths: type: array items: allOf: - - $ref: '#/components/schemas/ListedSandbox' - '401': - $ref: '#/components/responses/401' - '400': - $ref: '#/components/responses/400' - '500': - $ref: '#/components/responses/500' + - $ref: "#/components/schemas/ListedSandbox" + "401": + $ref: "#/components/responses/401" + "400": + $ref: "#/components/responses/400" + "500": + $ref: "#/components/responses/500" /sandboxes/metrics: get: @@ -1862,18 +1862,18 @@ paths: maxItems: 100 uniqueItems: true responses: - '200': + "200": description: Successfully returned all running sandboxes with metrics content: application/json: schema: - $ref: '#/components/schemas/SandboxesWithMetrics' - '401': - $ref: '#/components/responses/401' - '400': - $ref: '#/components/responses/400' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/SandboxesWithMetrics" + "401": + $ref: "#/components/responses/401" + "400": + $ref: "#/components/responses/400" + "500": + $ref: "#/components/responses/500" /sandboxes/{sandboxID}/logs: get: @@ -1884,7 +1884,7 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/sandboxID' + - $ref: "#/components/parameters/sandboxID" - in: query name: start schema: @@ -1901,18 +1901,18 @@ paths: type: integer description: Maximum number of logs that should be returned responses: - '200': + "200": description: Successfully returned the sandbox logs content: application/json: schema: - $ref: '#/components/schemas/SandboxLogs' - '404': - $ref: '#/components/responses/404' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/SandboxLogs" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /sandboxes/{sandboxID}: get: @@ -1923,20 +1923,20 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/sandboxID' + - $ref: "#/components/parameters/sandboxID" responses: - '200': + "200": description: Successfully returned the sandbox content: application/json: schema: - $ref: '#/components/schemas/SandboxDetail' - '404': - $ref: '#/components/responses/404' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/SandboxDetail" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" delete: description: Kill a sandbox @@ -1946,16 +1946,16 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/sandboxID' + - $ref: "#/components/parameters/sandboxID" responses: - '204': + "204": description: The sandbox was killed successfully - '404': - $ref: '#/components/responses/404' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /sandboxes/{sandboxID}/metrics: get: @@ -1966,7 +1966,7 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/sandboxID' + - $ref: "#/components/parameters/sandboxID" - in: query name: start schema: @@ -1983,22 +1983,22 @@ paths: description: Unix timestamp for the end of the interval, in seconds, for which the metrics responses: - '200': + "200": description: Successfully returned the sandbox metrics content: application/json: schema: type: array items: - $ref: '#/components/schemas/SandboxMetric' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/SandboxMetric" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" # TODO: Pause and resume might be exposed as POST /sandboxes/{sandboxID}/snapshot and then POST /sandboxes with specified snapshotting setup /sandboxes/{sandboxID}/pause: @@ -2010,18 +2010,18 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/sandboxID' + - $ref: "#/components/parameters/sandboxID" responses: - '204': + "204": description: The sandbox was paused successfully and can be resumed - '409': - $ref: '#/components/responses/409' - '404': - $ref: '#/components/responses/404' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + "409": + $ref: "#/components/responses/409" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /sandboxes/{sandboxID}/resume: post: @@ -2033,28 +2033,28 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/sandboxID' + - $ref: "#/components/parameters/sandboxID" requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ResumedSandbox' + $ref: "#/components/schemas/ResumedSandbox" responses: - '201': + "201": description: The sandbox was resumed successfully content: application/json: schema: - $ref: '#/components/schemas/Sandbox' - '409': - $ref: '#/components/responses/409' - '404': - $ref: '#/components/responses/404' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/Sandbox" + "409": + $ref: "#/components/responses/409" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /sandboxes/{sandboxID}/connect: post: @@ -2065,34 +2065,34 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/sandboxID' + - $ref: "#/components/parameters/sandboxID" requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ConnectSandbox' + $ref: "#/components/schemas/ConnectSandbox" responses: - '200': + "200": description: The sandbox was already running content: application/json: schema: - $ref: '#/components/schemas/Sandbox' - '201': + $ref: "#/components/schemas/Sandbox" + "201": description: The sandbox was resumed successfully content: application/json: schema: - $ref: '#/components/schemas/Sandbox' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/Sandbox" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" /sandboxes/{sandboxID}/timeout: post: @@ -2116,16 +2116,16 @@ paths: format: int32 minimum: 0 parameters: - - $ref: '#/components/parameters/sandboxID' + - $ref: "#/components/parameters/sandboxID" responses: - '204': + "204": description: Successfully set the sandbox timeout - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" /sandboxes/{sandboxID}/refreshes: post: @@ -2147,14 +2147,14 @@ paths: maximum: 3600 # 1 hour minimum: 0 parameters: - - $ref: '#/components/parameters/sandboxID' + - $ref: "#/components/parameters/sandboxID" responses: - '204': + "204": description: Successfully refreshed the sandbox - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" /v3/templates: post: @@ -2169,21 +2169,21 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/TemplateBuildRequestV3' + $ref: "#/components/schemas/TemplateBuildRequestV3" responses: - '202': + "202": description: The build was requested successfully content: application/json: schema: - $ref: '#/components/schemas/TemplateRequestResponseV3' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/TemplateRequestResponseV3" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /v2/templates: post: @@ -2199,21 +2199,21 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/TemplateBuildRequestV2' + $ref: "#/components/schemas/TemplateBuildRequestV2" responses: - '202': + "202": description: The build was requested successfully content: application/json: schema: - $ref: '#/components/schemas/TemplateLegacy' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/TemplateLegacy" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /templates/{templateID}/files/{hash}: get: @@ -2225,7 +2225,7 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/templateID' + - $ref: "#/components/parameters/templateID" - in: path name: hash required: true @@ -2234,20 +2234,20 @@ paths: description: Hash of the files responses: - '201': + "201": description: The upload link where to upload the tar file content: application/json: schema: - $ref: '#/components/schemas/TemplateBuildFileUpload' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/TemplateBuildFileUpload" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" /templates: get: @@ -2266,7 +2266,7 @@ paths: type: string description: Identifier of the team responses: - '200': + "200": description: Successfully returned all templates content: application/json: @@ -2274,11 +2274,11 @@ paths: type: array items: allOf: - - $ref: '#/components/schemas/Template' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + - $ref: "#/components/schemas/Template" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" post: description: Create a new template deprecated: true @@ -2292,21 +2292,21 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/TemplateBuildRequest' + $ref: "#/components/schemas/TemplateBuildRequest" responses: - '202': + "202": description: The build was accepted content: application/json: schema: - $ref: '#/components/schemas/TemplateLegacy' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/TemplateLegacy" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /templates/{templateID}: get: @@ -2317,20 +2317,20 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/templateID' - - $ref: '#/components/parameters/paginationNextToken' - - $ref: '#/components/parameters/paginationLimit' + - $ref: "#/components/parameters/templateID" + - $ref: "#/components/parameters/paginationNextToken" + - $ref: "#/components/parameters/paginationLimit" responses: - '200': + "200": description: Successfully returned the template with its builds content: application/json: schema: - $ref: '#/components/schemas/TemplateWithBuilds' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/TemplateWithBuilds" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" post: description: Rebuild an template deprecated: true @@ -2340,25 +2340,25 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/templateID' + - $ref: "#/components/parameters/templateID" requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/TemplateBuildRequest' + $ref: "#/components/schemas/TemplateBuildRequest" responses: - '202': + "202": description: The build was accepted content: application/json: schema: - $ref: '#/components/schemas/TemplateLegacy' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/TemplateLegacy" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" delete: description: Delete a template tags: [templates] @@ -2368,14 +2368,14 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/templateID' + - $ref: "#/components/parameters/templateID" responses: - '204': + "204": description: The template was deleted successfully - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" patch: description: Update template deprecated: true @@ -2386,22 +2386,22 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/templateID' + - $ref: "#/components/parameters/templateID" requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/TemplateUpdateRequest' + $ref: "#/components/schemas/TemplateUpdateRequest" responses: - '200': + "200": description: The template was updated successfully - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /templates/{templateID}/builds/{buildID}: post: @@ -2413,15 +2413,15 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/templateID' - - $ref: '#/components/parameters/buildID' + - $ref: "#/components/parameters/templateID" + - $ref: "#/components/parameters/buildID" responses: - '202': + "202": description: The build has started - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /v2/templates/{templateID}/builds/{buildID}: post: @@ -2432,21 +2432,21 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/templateID' - - $ref: '#/components/parameters/buildID' + - $ref: "#/components/parameters/templateID" + - $ref: "#/components/parameters/buildID" requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/TemplateBuildStartV2' + $ref: "#/components/schemas/TemplateBuildStartV2" responses: - '202': + "202": description: The build has started - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /v2/templates/{templateID}: patch: @@ -2458,26 +2458,26 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/templateID' + - $ref: "#/components/parameters/templateID" requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/TemplateUpdateRequest' + $ref: "#/components/schemas/TemplateUpdateRequest" responses: - '200': + "200": description: The template was updated successfully content: application/json: schema: - $ref: '#/components/schemas/TemplateUpdateResponse' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/TemplateUpdateResponse" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /templates/{templateID}/builds/{buildID}/status: get: @@ -2489,8 +2489,8 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/templateID' - - $ref: '#/components/parameters/buildID' + - $ref: "#/components/parameters/templateID" + - $ref: "#/components/parameters/buildID" - in: query name: logsOffset schema: @@ -2511,20 +2511,20 @@ paths: - in: query name: level schema: - $ref: '#/components/schemas/LogLevel' + $ref: "#/components/schemas/LogLevel" responses: - '200': + "200": description: Successfully returned the template content: application/json: schema: - $ref: '#/components/schemas/TemplateBuildInfo' - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/TemplateBuildInfo" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" /templates/{templateID}/builds/{buildID}/logs: get: @@ -2536,8 +2536,8 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/templateID' - - $ref: '#/components/parameters/buildID' + - $ref: "#/components/parameters/templateID" + - $ref: "#/components/parameters/buildID" - in: query name: cursor schema: @@ -2557,29 +2557,29 @@ paths: - in: query name: direction schema: - $ref: '#/components/schemas/LogsDirection' + $ref: "#/components/schemas/LogsDirection" - in: query name: level schema: - $ref: '#/components/schemas/LogLevel' + $ref: "#/components/schemas/LogLevel" - in: query name: source schema: - $ref: '#/components/schemas/LogsSource' + $ref: "#/components/schemas/LogsSource" description: Source of the logs that should be returned from responses: - '200': + "200": description: Successfully returned the template build logs content: application/json: schema: - $ref: '#/components/schemas/TemplateBuildLogsResponse' - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/TemplateBuildLogsResponse" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" /templates/tags: post: @@ -2594,22 +2594,22 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AssignTemplateTagsRequest' + $ref: "#/components/schemas/AssignTemplateTagsRequest" responses: - '201': + "201": description: Tag assigned successfully content: application/json: schema: - $ref: '#/components/schemas/AssignedTemplateTags' - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/AssignedTemplateTags" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" delete: description: Delete multiple tags from templates tags: [tags] @@ -2622,18 +2622,18 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DeleteTemplateTagsRequest' + $ref: "#/components/schemas/DeleteTemplateTagsRequest" responses: - '204': + "204": description: Tags deleted successfully - '400': - $ref: '#/components/responses/400' - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" /templates/aliases/{alias}: get: @@ -2651,20 +2651,20 @@ paths: type: string description: Template alias responses: - '200': + "200": description: Successfully queried template by alias content: application/json: schema: - $ref: '#/components/schemas/TemplateAliasResponse' - '400': - $ref: '#/components/responses/400' - '403': - $ref: '#/components/responses/403' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/TemplateAliasResponse" + "400": + $ref: "#/components/responses/400" + "403": + $ref: "#/components/responses/403" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" /nodes: get: @@ -2673,7 +2673,7 @@ paths: security: - AdminTokenAuth: [] responses: - '200': + "200": description: Successfully returned all nodes content: application/json: @@ -2681,11 +2681,11 @@ paths: type: array items: allOf: - - $ref: '#/components/schemas/Node' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + - $ref: "#/components/schemas/Node" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /nodes/{nodeID}: get: @@ -2694,7 +2694,7 @@ paths: security: - AdminTokenAuth: [] parameters: - - $ref: '#/components/parameters/nodeID' + - $ref: "#/components/parameters/nodeID" - in: query name: clusterID description: Identifier of the cluster @@ -2703,39 +2703,39 @@ paths: type: string format: uuid responses: - '200': + "200": description: Successfully returned the node content: application/json: schema: - $ref: '#/components/schemas/NodeDetail' - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/NodeDetail" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" post: description: Change status of a node tags: [admin] security: - AdminTokenAuth: [] parameters: - - $ref: '#/components/parameters/nodeID' + - $ref: "#/components/parameters/nodeID" requestBody: content: application/json: schema: - $ref: '#/components/schemas/NodeStatusChange' + $ref: "#/components/schemas/NodeStatusChange" responses: - '204': + "204": description: The node status was changed successfully - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" /admin/teams/{teamID}/sandboxes/kill: post: @@ -2753,18 +2753,18 @@ paths: format: uuid description: Team ID responses: - '200': + "200": description: Successfully killed sandboxes content: application/json: schema: - $ref: '#/components/schemas/AdminSandboxKillResult' - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/AdminSandboxKillResult" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" /access-tokens: post: @@ -2777,18 +2777,18 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/NewAccessToken' + $ref: "#/components/schemas/NewAccessToken" responses: - '201': + "201": description: Access token created successfully content: application/json: schema: - $ref: '#/components/schemas/CreatedAccessToken' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/CreatedAccessToken" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /access-tokens/{accessTokenID}: delete: @@ -2797,16 +2797,16 @@ paths: security: - Supabase1TokenAuth: [] parameters: - - $ref: '#/components/parameters/accessTokenID' + - $ref: "#/components/parameters/accessTokenID" responses: - '204': + "204": description: Access token deleted successfully - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" /api-keys: get: @@ -2816,18 +2816,18 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] responses: - '200': + "200": description: Successfully returned all team API keys content: application/json: schema: type: array items: - $ref: '#/components/schemas/TeamAPIKey' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/TeamAPIKey" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" post: description: Create a new team API key tags: [api-keys] @@ -2839,18 +2839,18 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/NewTeamAPIKey' + $ref: "#/components/schemas/NewTeamAPIKey" responses: - '201': + "201": description: Team API key created successfully content: application/json: schema: - $ref: '#/components/schemas/CreatedTeamAPIKey' - '401': - $ref: '#/components/responses/401' - '500': - $ref: '#/components/responses/500' + $ref: "#/components/schemas/CreatedTeamAPIKey" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" /api-keys/{apiKeyID}: patch: @@ -2860,22 +2860,22 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/apiKeyID' + - $ref: "#/components/parameters/apiKeyID" requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/UpdateTeamAPIKey' + $ref: "#/components/schemas/UpdateTeamAPIKey" responses: - '200': + "200": description: Team API key updated successfully - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" delete: description: Delete a team API key tags: [api-keys] @@ -2883,13 +2883,13 @@ paths: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] parameters: - - $ref: '#/components/parameters/apiKeyID' + - $ref: "#/components/parameters/apiKeyID" responses: - '204': + "204": description: Team API key deleted successfully - '401': - $ref: '#/components/responses/401' - '404': - $ref: '#/components/responses/404' - '500': - $ref: '#/components/responses/500' + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" \ No newline at end of file diff --git a/src/server/api/models/builds.models.ts b/src/server/api/models/builds.models.ts index 48023c77f..0da3eacb1 100644 --- a/src/server/api/models/builds.models.ts +++ b/src/server/api/models/builds.models.ts @@ -50,18 +50,17 @@ export interface BuildDetailsDTO { type RawListedBuildWithEnvAndAliasesDB = { id: string - env_id: string status: string reason: unknown created_at: string finished_at: string | null - envs: { - id: string - team_id: string - env_aliases: Array<{ alias: string }> | null - } + env_build_assignments: Array<{ + env_id: string + }> } +const EMPTY_ALIAS_BY_ENV_ID: ReadonlyMap = new Map() + // mappings export function checkIfBuildStillHasLogs(createdAt: number): boolean { @@ -80,14 +79,17 @@ export function mapDatabaseBuildReasonToListedBuildDTOStatusMessage( } export function mapDatabaseBuildToListedBuildDTO( - build: RawListedBuildWithEnvAndAliasesDB + build: RawListedBuildWithEnvAndAliasesDB, + aliasesByEnvId: ReadonlyMap = EMPTY_ALIAS_BY_ENV_ID ): ListedBuildDTO { - const alias = build.envs.env_aliases?.[0]?.alias + const assignment = build.env_build_assignments[0] + const envId = assignment?.env_id ?? build.id + const alias = aliasesByEnvId.get(envId) return { id: build.id, - template: alias ?? build.env_id, - templateId: build.env_id, + template: alias ?? envId, + templateId: envId, status: mapDatabaseBuildStatusToBuildStatusDTO( build.status as BuildStatusDB ), diff --git a/src/server/api/repositories/builds.repository.ts b/src/server/api/repositories/builds.repository.ts index 7dc0d2740..cb7a6a7c6 100644 --- a/src/server/api/repositories/builds.repository.ts +++ b/src/server/api/repositories/builds.repository.ts @@ -16,33 +16,136 @@ import { // helpers +const ALL_BUILD_STATUSES: BuildStatusDB[] = [ + 'waiting', + 'building', + 'uploaded', + 'failed', +] + function isUUID(value: string): boolean { return z.uuid().safeParse(value).success } -async function resolveTemplateId( - templateIdOrAlias: string, - teamId: string -): Promise { - const { data: envById } = await supabaseAdmin +async function getTeamEnvIds(teamId: string): Promise { + const { data, error } = await supabaseAdmin .from('envs') .select('id') - .eq('id', templateIdOrAlias) .eq('team_id', teamId) - .maybeSingle() - if (envById) return envById.id + if (error) { + throw error + } - const { data: envByAlias } = await supabaseAdmin + return data?.map((env) => env.id) ?? [] +} + +async function resolveTemplateId( + templateIdOrAlias: string, + teamEnvIds: string[] +): Promise { + if (teamEnvIds.includes(templateIdOrAlias)) { + return templateIdOrAlias + } + + if (teamEnvIds.length === 0) { + return null + } + + const { data: envByAlias, error } = await supabaseAdmin .from('env_aliases') - .select('env_id, envs!inner(team_id)') + .select('env_id') .eq('alias', templateIdOrAlias) - .eq('envs.team_id', teamId) + .in('env_id', teamEnvIds) .maybeSingle() + if (error) { + throw error + } + return envByAlias?.env_id ?? null } +function buildListBuildsQuery( + teamEnvIds: string[], + statuses: BuildStatusDB[], + cursor?: string +) { + let query = supabaseAdmin + .from('env_builds') + .select( + ` + id, + status, + reason, + created_at, + finished_at, + env_build_assignments!inner( + env_id + ) + ` + ) + .in('env_build_assignments.env_id', teamEnvIds) + .order('created_at', { ascending: false }) + + if (statuses.length < ALL_BUILD_STATUSES.length) { + query = query.in('status', statuses) + } + + if (cursor) { + query = query.lt('created_at', cursor) + } + + return query +} + +function mergeAndSortBuildRows( + first: T[], + second: T[] +): T[] { + const uniqueById = new Map() + + for (const build of first) { + uniqueById.set(build.id, build) + } + + for (const build of second) { + uniqueById.set(build.id, build) + } + + return [...uniqueById.values()].sort((a, b) => + b.created_at.localeCompare(a.created_at) + ) +} + +async function fetchAliasesByEnvId( + envIds: string[] +): Promise> { + if (envIds.length === 0) { + return new Map() + } + + const { data, error } = await supabaseAdmin + .from('env_aliases') + .select('id, env_id, alias') + .in('env_id', envIds) + .order('id', { ascending: true }) + + if (error) { + throw error + } + + const aliasesByEnvId = new Map() + + for (const alias of data ?? []) { + if (!aliasesByEnvId.has(alias.env_id)) { + aliasesByEnvId.set(alias.env_id, alias.alias) + } + } + + return aliasesByEnvId +} + // list builds interface ListBuildsOptions { @@ -58,34 +161,31 @@ interface ListBuildsResult { async function listBuilds( teamId: string, buildIdOrTemplate?: string, - statuses: BuildStatusDB[] = ['waiting', 'building', 'uploaded', 'failed'], + statuses: BuildStatusDB[] = ALL_BUILD_STATUSES, options: ListBuildsOptions = {} ): Promise { const limit = options.limit ?? 50 + const queryLimit = limit + 1 + const teamEnvIds = await getTeamEnvIds(teamId) - let query = supabaseAdmin - .from('env_builds') - .select( - ` - id, - env_id, - status, - reason, - created_at, - finished_at, - envs!inner( - id, - team_id, - env_aliases(alias) - ) - ` - ) - .eq('envs.team_id', teamId) - .in('status', statuses) - .order('created_at', { ascending: false }) + if (teamEnvIds.length === 0) { + return { + data: [], + nextCursor: null, + } + } + + let rawBuilds: Array<{ + id: string + status: string + reason: unknown + created_at: string + finished_at: string | null + env_build_assignments: Array<{ env_id: string }> + }> = [] if (buildIdOrTemplate) { - const resolvedEnvId = await resolveTemplateId(buildIdOrTemplate, teamId) + const resolvedEnvId = await resolveTemplateId(buildIdOrTemplate, teamEnvIds) const isBuildUUID = isUUID(buildIdOrTemplate) if (!resolvedEnvId && !isBuildUUID) { @@ -96,27 +196,71 @@ async function listBuilds( } if (resolvedEnvId && isBuildUUID) { - query = query.or(`env_id.eq.${resolvedEnvId},id.eq.${buildIdOrTemplate}`) + const [buildsByTemplateResult, buildByIdResult] = await Promise.all([ + buildListBuildsQuery(teamEnvIds, statuses, options.cursor) + .eq('env_build_assignments.env_id', resolvedEnvId) + .limit(queryLimit), + buildListBuildsQuery(teamEnvIds, statuses, options.cursor) + .eq('id', buildIdOrTemplate) + .limit(1), + ]) + + if (buildsByTemplateResult.error) { + throw buildsByTemplateResult.error + } + + if (buildByIdResult.error) { + throw buildByIdResult.error + } + + rawBuilds = mergeAndSortBuildRows( + buildsByTemplateResult.data ?? [], + buildByIdResult.data ?? [] + ) } else if (resolvedEnvId) { - query = query.eq('env_id', resolvedEnvId) - } else if (isBuildUUID) { - query = query.eq('id', buildIdOrTemplate) - } - } + const { data, error } = await buildListBuildsQuery( + teamEnvIds, + statuses, + options.cursor + ) + .eq('env_build_assignments.env_id', resolvedEnvId) + .limit(queryLimit) - if (options.cursor) { - query = query.lt('created_at', options.cursor) - } + if (error) { + throw error + } - query = query.limit(limit + 1) + rawBuilds = data ?? [] + } else if (isBuildUUID) { + const { data, error } = await buildListBuildsQuery( + teamEnvIds, + statuses, + options.cursor + ) + .eq('id', buildIdOrTemplate) + .limit(queryLimit) - const { data: rawBuilds, error } = await query + if (error) { + throw error + } - if (error) { - throw error + rawBuilds = data ?? [] + } + } else { + const { data, error } = await buildListBuildsQuery( + teamEnvIds, + statuses, + options.cursor + ).limit(queryLimit) + + if (error) { + throw error + } + + rawBuilds = data ?? [] } - if (!rawBuilds || rawBuilds.length === 0) { + if (rawBuilds.length === 0) { return { data: [], nextCursor: null, @@ -125,9 +269,19 @@ async function listBuilds( const hasMore = rawBuilds.length > limit const trimmedBuilds = hasMore ? rawBuilds.slice(0, limit) : rawBuilds + const envIds = [ + ...new Set( + trimmedBuilds + .map((build) => build.env_build_assignments[0]?.env_id) + .filter((envId): envId is string => Boolean(envId)) + ), + ] + const aliasesByEnvId = await fetchAliasesByEnvId(envIds) return { - data: trimmedBuilds.map(mapDatabaseBuildToListedBuildDTO), + data: trimmedBuilds.map((build) => + mapDatabaseBuildToListedBuildDTO(build, aliasesByEnvId) + ), nextCursor: hasMore ? trimmedBuilds[trimmedBuilds.length - 1]!.created_at : null, @@ -144,10 +298,16 @@ async function getRunningStatuses( return [] } + const teamEnvIds = await getTeamEnvIds(teamId) + + if (teamEnvIds.length === 0) { + return [] + } + const { data, error } = await supabaseAdmin .from('env_builds') - .select('id, status, reason, finished_at, envs!inner(team_id)') - .eq('envs.team_id', teamId) + .select('id, status, reason, finished_at, env_build_assignments!inner(env_id)') + .in('env_build_assignments.env_id', teamEnvIds) .in('id', buildIds) if (error) throw error @@ -170,13 +330,30 @@ async function getRunningStatuses( // get build details export async function getBuildInfo(buildId: string, teamId: string) { + const teamEnvIds = await getTeamEnvIds(teamId) + + if (teamEnvIds.length === 0) { + throw new TRPCError({ + code: 'NOT_FOUND', + message: "Build not found or you don't have access to it", + }) + } + const { data, error } = await supabaseAdmin .from('env_builds') .select( - 'created_at, finished_at, status, reason, envs!inner(team_id, env_aliases(alias))' + ` + created_at, + finished_at, + status, + reason, + env_build_assignments!inner( + env_id + ) + ` ) .eq('id', buildId) - .eq('envs.team_id', teamId) + .in('env_build_assignments.env_id', teamEnvIds) .maybeSingle() if (error) { @@ -205,7 +382,24 @@ export async function getBuildInfo(buildId: string, teamId: string) { }) } - const alias = data.envs.env_aliases?.[0]?.alias + const envId = data.env_build_assignments[0]?.env_id + let alias: string | undefined + + if (envId) { + const { data: aliasData, error: aliasError } = await supabaseAdmin + .from('env_aliases') + .select('alias') + .eq('env_id', envId) + .order('id', { ascending: true }) + .limit(1) + .maybeSingle() + + if (aliasError) { + throw aliasError + } + + alias = aliasData?.alias + } return { alias, diff --git a/src/types/database.types.ts b/src/types/database.types.ts index b0e743b42..5dfe2b6c6 100644 --- a/src/types/database.types.ts +++ b/src/types/database.types.ts @@ -7,11 +7,6 @@ export type Json = | Json[] export type Database = { - // Allows to automatically instantiate createClient with right options - // instead of createClient(URL, KEY) - __InternalSupabase: { - PostgrestVersion: "10.2.0 (e07807d)" - } public: { Tables: { _migrations: { @@ -73,7 +68,6 @@ export type Database = { { foreignKeyName: "access_tokens_users_access_tokens" columns: ["user_id"] - isOneToOne: false referencedRelation: "auth_users" referencedColumns: ["id"] }, @@ -129,21 +123,18 @@ export type Database = { { foreignKeyName: "addons_teams_addons" columns: ["team_id"] - isOneToOne: false referencedRelation: "team_limits" referencedColumns: ["id"] }, { foreignKeyName: "addons_teams_addons" columns: ["team_id"] - isOneToOne: false referencedRelation: "teams" referencedColumns: ["id"] }, { foreignKeyName: "addons_users_addons" columns: ["added_by"] - isOneToOne: false referencedRelation: "auth_users" referencedColumns: ["id"] }, @@ -199,7 +190,6 @@ export type Database = { { foreignKeyName: "env_aliases_envs_env_aliases" columns: ["env_id"] - isOneToOne: false referencedRelation: "envs" referencedColumns: ["id"] }, @@ -234,14 +224,12 @@ export type Database = { { foreignKeyName: "fk_env_build_assignments_build" columns: ["build_id"] - isOneToOne: false referencedRelation: "env_builds" referencedColumns: ["id"] }, { foreignKeyName: "fk_env_build_assignments_env" columns: ["env_id"] - isOneToOne: false referencedRelation: "envs" referencedColumns: ["id"] }, @@ -330,7 +318,6 @@ export type Database = { { foreignKeyName: "env_builds_envs_builds" columns: ["env_id"] - isOneToOne: false referencedRelation: "envs" referencedColumns: ["id"] }, @@ -353,7 +340,6 @@ export type Database = { { foreignKeyName: "env_defaults_env_id_fkey" columns: ["env_id"] - isOneToOne: true referencedRelation: "envs" referencedColumns: ["id"] }, @@ -400,28 +386,24 @@ export type Database = { { foreignKeyName: "envs_cluster_id_fkey" columns: ["cluster_id"] - isOneToOne: false referencedRelation: "clusters" referencedColumns: ["id"] }, { foreignKeyName: "envs_teams_envs" columns: ["team_id"] - isOneToOne: false referencedRelation: "team_limits" referencedColumns: ["id"] }, { foreignKeyName: "envs_teams_envs" columns: ["team_id"] - isOneToOne: false referencedRelation: "teams" referencedColumns: ["id"] }, { foreignKeyName: "envs_users_created_envs" columns: ["created_by"] - isOneToOne: false referencedRelation: "auth_users" referencedColumns: ["id"] }, @@ -453,7 +435,6 @@ export type Database = { { foreignKeyName: "feedback_user_id_fkey" columns: ["user_id"] - isOneToOne: false referencedRelation: "auth_users" referencedColumns: ["id"] }, @@ -509,28 +490,24 @@ export type Database = { { foreignKeyName: "fk_snapshots_team" columns: ["team_id"] - isOneToOne: false referencedRelation: "team_limits" referencedColumns: ["id"] }, { foreignKeyName: "fk_snapshots_team" columns: ["team_id"] - isOneToOne: false referencedRelation: "teams" referencedColumns: ["id"] }, { foreignKeyName: "snapshots_envs_base_env_id" columns: ["base_env_id"] - isOneToOne: false referencedRelation: "envs" referencedColumns: ["id"] }, { foreignKeyName: "snapshots_envs_env_id" columns: ["env_id"] - isOneToOne: false referencedRelation: "envs" referencedColumns: ["id"] }, @@ -583,21 +560,18 @@ export type Database = { { foreignKeyName: "team_api_keys_teams_team_api_keys" columns: ["team_id"] - isOneToOne: false referencedRelation: "team_limits" referencedColumns: ["id"] }, { foreignKeyName: "team_api_keys_teams_team_api_keys" columns: ["team_id"] - isOneToOne: false referencedRelation: "teams" referencedColumns: ["id"] }, { foreignKeyName: "team_api_keys_users_created_api_keys" columns: ["created_by"] - isOneToOne: false referencedRelation: "auth_users" referencedColumns: ["id"] }, @@ -650,14 +624,12 @@ export type Database = { { foreignKeyName: "teams_cluster_id_fkey" columns: ["cluster_id"] - isOneToOne: false referencedRelation: "clusters" referencedColumns: ["id"] }, { foreignKeyName: "teams_tiers_teams" columns: ["tier"] - isOneToOne: false referencedRelation: "tiers" referencedColumns: ["id"] }, @@ -722,7 +694,6 @@ export type Database = { { foreignKeyName: "users_id_fkey" columns: ["id"] - isOneToOne: true referencedRelation: "auth_users" referencedColumns: ["id"] }, @@ -757,28 +728,24 @@ export type Database = { { foreignKeyName: "users_teams_added_by_user" columns: ["added_by"] - isOneToOne: false referencedRelation: "auth_users" referencedColumns: ["id"] }, { foreignKeyName: "users_teams_teams_teams" columns: ["team_id"] - isOneToOne: false referencedRelation: "team_limits" referencedColumns: ["id"] }, { foreignKeyName: "users_teams_teams_teams" columns: ["team_id"] - isOneToOne: false referencedRelation: "teams" referencedColumns: ["id"] }, { foreignKeyName: "users_teams_users_users" columns: ["user_id"] - isOneToOne: false referencedRelation: "auth_users" referencedColumns: ["id"] },