From a3fbe72717ee213e0cabf36cebbefd3a8130a82e Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 22 Dec 2022 16:34:29 +0100 Subject: [PATCH 01/61] docs: audit, deploymentconfig, files, parameters --- coderd/apidoc/docs.go | 1491 ++++++++-- coderd/apidoc/swagger.json | 1119 ++++++- coderd/audit.go | 19 + coderd/deploymentconfig.go | 7 + coderd/files.go | 18 + coderd/parameters.go | 30 + coderd/templates.go | 2 +- codersdk/audit.go | 4 +- codersdk/parameters.go | 16 +- codersdk/workspacebuilds.go | 2 +- docs/api/audit.md | 129 + docs/api/files.md | 77 + docs/api/general.md | 863 +++++- docs/api/parameters.md | 208 ++ docs/api/schemas.md | 2579 ++++++++++++++++- docs/api/templates.md | 68 +- docs/api/workspaces.md | 20 +- docs/manifest.json | 12 + .../markdown-template/code_shell.dot | 6 +- scripts/apidocgen/markdown-template/main.dot | 3 - .../apidocgen/markdown-template/operation.dot | 2 - 21 files changed, 6334 insertions(+), 341 deletions(-) create mode 100644 docs/api/audit.md create mode 100644 docs/api/files.md create mode 100644 docs/api/parameters.md diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index e674cf6222bdd..5ff12f9fb8f8f 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -97,6 +97,92 @@ const docTemplate = `{ } } }, + "/audit": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Audit" + ], + "summary": "Get audit logs", + "operationId": "get-audit-logs", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "q", + "in": "query", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "After ID", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AuditLogResponse" + } + } + } + } + }, + "/audit/testgenerate": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "tags": [ + "Audit" + ], + "summary": "Generate fake audit log", + "operationId": "generate-fake-audit-logs", + "parameters": [ + { + "description": "Audit log request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTestAuditLogRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, "/authcheck": { "post": { "security": [ @@ -156,6 +242,31 @@ const docTemplate = `{ } } }, + "/config/deployment": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "General" + ], + "summary": "Get deployment config", + "operationId": "get-deployment-config", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.DeploymentConfig" + } + } + } + } + }, "/csp/reports": { "post": { "security": [ @@ -192,6 +303,81 @@ const docTemplate = `{ } } }, + "/files": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "description": "Notice: Swagger 2.0 doesn't support file upload with a ` + "`" + `content-type` + "`" + ` different than ` + "`" + `application/x-www-form-urlencoded` + "`" + `.", + "consumes": [ + "application/x-tar" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Files" + ], + "summary": "Upload file", + "operationId": "update-file", + "parameters": [ + { + "type": "string", + "default": "application/x-tar", + "description": "Content-Type must be ` + "`" + `application/x-tar` + "`" + `", + "name": "Content-Type", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "File to be uploaded", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.UploadResponse" + } + } + } + } + }, + "/files/{fileID}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "tags": [ + "Files" + ], + "summary": "Get file by ID", + "operationId": "get-file-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "File ID", + "name": "fileID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/organizations/{organization-id}/templates/": { "post": { "security": [ @@ -360,7 +546,7 @@ const docTemplate = `{ } } }, - "/templates/{id}": { + "/parameters/{scope}/{id}": { "get": { "security": [ { @@ -371,15 +557,27 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Templates" + "Parameters" ], - "summary": "Update template metadata by ID", - "operationId": "update-template-metadata", + "summary": "Get parameters", + "operationId": "get-parameters", "parameters": [ + { + "enum": [ + "template", + "workspace", + "import_job" + ], + "type": "string", + "description": "Scope", + "name": "scope", + "in": "path", + "required": true + }, { "type": "string", "format": "uuid", - "description": "Template ID", + "description": "ID", "name": "id", "in": "path", "required": true @@ -389,66 +587,127 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Template" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Parameter" + } } } } }, - "delete": { + "post": { "security": [ { "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Templates" + "Parameters" ], - "summary": "Delete template by ID", - "operationId": "delete-template-by-id", + "summary": "Create parameter", + "operationId": "create-parameter", "parameters": [ + { + "description": "Parameter request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateParameterRequest" + } + }, + { + "enum": [ + "template", + "workspace", + "import_job" + ], + "type": "string", + "description": "Scope", + "name": "scope", + "in": "path", + "required": true + }, { "type": "string", "format": "uuid", - "description": "Template ID", + "description": "ID", "name": "id", "in": "path", "required": true } ], "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.Parameter" } } } } }, - "/updatecheck": { - "get": { + "/parameters/{scope}/{id}/{name}": { + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], "produces": [ "application/json" ], "tags": [ - "General" + "Parameters" + ], + "summary": "Delete parameter", + "operationId": "delete-parameter", + "parameters": [ + { + "enum": [ + "template", + "workspace", + "import_job" + ], + "type": "string", + "description": "Scope", + "name": "scope", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Name", + "name": "name", + "in": "path", + "required": true + } ], - "summary": "Update check", - "operationId": "update-check", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.UpdateCheckResponse" + "$ref": "#/definitions/codersdk.Response" } } } } }, - "/users/{user}/workspace/{workspacename}": { + "/templates/{id}": { "get": { "security": [ { @@ -459,44 +718,30 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Workspaces" + "Templates" ], - "summary": "Get workspace metadata by owner and workspace name", - "operationId": "get-workspace-metadata-by-owner-and-workspace-name", + "summary": "Get template metadata by ID", + "operationId": "get-template-metadata-by-id", "parameters": [ { "type": "string", - "description": "Owner username", - "name": "user", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Workspace name", - "name": "workspacename", + "format": "uuid", + "description": "Template ID", + "name": "id", "in": "path", "required": true - }, - { - "type": "boolean", - "description": "Return data instead of HTTP 404 if the workspace is deleted", - "name": "include_deleted", - "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Workspace" + "$ref": "#/definitions/codersdk.Template" } } } - } - }, - "/workspaces": { - "get": { + }, + "delete": { "security": [ { "CoderSessionToken": [] @@ -506,17 +751,152 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Workspaces" + "Templates" ], - "summary": "List workspaces", - "operationId": "get-workspaces", + "summary": "Delete template by ID", + "operationId": "delete-template-by-id", "parameters": [ { "type": "string", - "description": "Filter by owner username", - "name": "owner", - "in": "query" - }, + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + }, + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Update template metadata by ID", + "operationId": "update-template-metadata", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Template" + } + } + } + } + }, + "/updatecheck": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "General" + ], + "summary": "Update check", + "operationId": "update-check", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UpdateCheckResponse" + } + } + } + } + }, + "/users/{user}/workspace/{workspacename}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Workspaces" + ], + "summary": "Get workspace metadata by owner and workspace name", + "operationId": "get-workspace-metadata-by-owner-and-workspace-name", + "parameters": [ + { + "type": "string", + "description": "Owner username", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Workspace name", + "name": "workspacename", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "Return data instead of HTTP 404 if the workspace is deleted", + "name": "include_deleted", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Workspace" + } + } + } + } + }, + "/workspaces": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Workspaces" + ], + "summary": "List workspaces", + "operationId": "get-workspaces", + "parameters": [ + { + "type": "string", + "description": "Filter by owner username", + "name": "owner", + "in": "query" + }, { "type": "string", "description": "Filter by template name", @@ -835,6 +1215,99 @@ const docTemplate = `{ } } }, + "codersdk.AuditDiff": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/codersdk.AuditDiffField" + } + }, + "codersdk.AuditDiffField": { + "type": "object", + "properties": { + "new": {}, + "old": {}, + "secret": { + "type": "boolean" + } + } + }, + "codersdk.AuditLog": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "additional_fields": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": { + "type": "string" + }, + "diff": { + "$ref": "#/definitions/codersdk.AuditDiff" + }, + "id": { + "type": "string" + }, + "ip": { + "$ref": "#/definitions/netip.Addr" + }, + "is_deleted": { + "type": "boolean" + }, + "organization_id": { + "type": "string" + }, + "request_id": { + "type": "string" + }, + "resource_icon": { + "type": "string" + }, + "resource_id": { + "type": "string" + }, + "resource_link": { + "type": "string" + }, + "resource_target": { + "description": "ResourceTarget is the name of the resource.", + "type": "string" + }, + "resource_type": { + "type": "string" + }, + "status_code": { + "type": "integer" + }, + "time": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/codersdk.User" + }, + "user_agent": { + "type": "string" + } + } + }, + "codersdk.AuditLogResponse": { + "type": "object", + "properties": { + "audit_logs": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.AuditLog" + } + }, + "count": { + "type": "integer" + } + } + }, "codersdk.AuthorizationCheck": { "description": "AuthorizationCheck is used to check if the currently authenticated user (or the specified user) can do a given action to a given set of objects.", "type": "object", @@ -854,183 +1327,739 @@ const docTemplate = `{ } } }, - "codersdk.AuthorizationObject": { - "description": "AuthorizationObject can represent a \"set\" of objects, such as: all workspaces in an organization, all workspaces owned by me, all workspaces across the entire product.", + "codersdk.AuthorizationObject": { + "description": "AuthorizationObject can represent a \"set\" of objects, such as: all workspaces in an organization, all workspaces owned by me, all workspaces across the entire product.", + "type": "object", + "properties": { + "organization_id": { + "description": "OrganizationID (optional) adds the set constraint to all resources owned by a given organization.", + "type": "string" + }, + "owner_id": { + "description": "OwnerID (optional) adds the set constraint to all resources owned by a given user.", + "type": "string" + }, + "resource_id": { + "description": "ResourceID (optional) reduces the set to a singular resource. This assigns\na resource ID to the resource type, eg: a single workspace.\nThe rbac library will not fetch the resource from the database, so if you\nare using this option, you should also set the owner ID and organization ID\nif possible. Be as specific as possible using all the fields relevant.", + "type": "string" + }, + "resource_type": { + "description": "ResourceType is the name of the resource.\n` + "`" + `./coderd/rbac/object.go` + "`" + ` has the list of valid resource types.", + "type": "string" + } + } + }, + "codersdk.AuthorizationRequest": { + "type": "object", + "properties": { + "checks": { + "description": "Checks is a map keyed with an arbitrary string to a permission check.\nThe key can be any string that is helpful to the caller, and allows\nmultiple permission checks to be run in a single request.\nThe key ensures that each permission check has the same key in the\nresponse.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/codersdk.AuthorizationCheck" + } + } + } + }, + "codersdk.AuthorizationResponse": { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, + "codersdk.BuildInfoResponse": { + "type": "object", + "properties": { + "external_url": { + "description": "ExternalURL references the current Coder version.\nFor production builds, this will link directly to a release. For development builds, this will link to a commit.", + "type": "string" + }, + "version": { + "description": "Version returns the semantic version of the build.", + "type": "string" + } + } + }, + "codersdk.CreateParameterRequest": { + "description": "CreateParameterRequest is a structure used to create a new parameter value for a scope.", + "type": "object", + "required": [ + "destination_scheme", + "name", + "source_scheme", + "source_value" + ], + "properties": { + "copy_from_parameter": { + "description": "CloneID allows copying the value of another parameter.\nThe other param must be related to the same template_id for this to\nsucceed.\nNo other fields are required if using this, as all fields will be copied\nfrom the other parameter.", + "type": "string" + }, + "destination_scheme": { + "type": "string", + "enum": [ + "none", + "environment_variable", + "provisioner_variable" + ] + }, + "name": { + "type": "string" + }, + "source_scheme": { + "type": "string", + "enum": [ + "none", + "data" + ] + }, + "source_value": { + "type": "string" + } + } + }, + "codersdk.CreateTemplateRequest": { + "type": "object", + "required": [ + "name", + "template_version_id" + ], + "properties": { + "allow_user_cancel_workspace_jobs": { + "description": "Allow users to cancel in-progress workspace jobs.\n*bool as the default value is \"true\".", + "type": "boolean" + }, + "default_ttl_ms": { + "description": "DefaultTTLMillis allows optionally specifying the default TTL\nfor all workspaces created from this template.", + "type": "integer" + }, + "description": { + "description": "Description is a description of what the template contains. It must be\nless than 128 bytes.", + "type": "string" + }, + "display_name": { + "description": "DisplayName is the displayed name of the template.", + "type": "string" + }, + "icon": { + "description": "Icon is a relative path or external URL that specifies\nan icon to be displayed in the dashboard.", + "type": "string" + }, + "name": { + "description": "Name is the name of the template.", + "type": "string" + }, + "parameter_values": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.CreateParameterRequest" + } + }, + "template_version_id": { + "description": "VersionID is an in-progress or completed job to use as an initial version\nof the template.\n\nThis is required on creation to enable a user-flow of validating a\ntemplate works. There is no reason the data-model cannot support empty\ntemplates, but it doesn't make sense for users.", + "type": "string" + } + } + }, + "codersdk.CreateTestAuditLogRequest": { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": [ + "create", + "write", + "delete", + "start", + "stop" + ] + }, + "resource_id": { + "type": "string" + }, + "resource_type": { + "type": "string", + "enum": [ + "organization", + "template", + "template_version", + "user", + "workspace", + "workspace_build", + "git_ssh_key", + "api_key", + "group" + ] + }, + "time": { + "type": "string" + } + } + }, + "codersdk.DERP": { + "type": "object", + "properties": { + "config": { + "$ref": "#/definitions/codersdk.DERPConfig" + }, + "server": { + "$ref": "#/definitions/codersdk.DERPServerConfig" + } + } + }, + "codersdk.DERPConfig": { + "type": "object", + "properties": { + "path": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + } + } + }, + "codersdk.DERPRegion": { + "type": "object", + "properties": { + "latency_ms": { + "type": "number" + }, + "preferred": { + "type": "boolean" + } + } + }, + "codersdk.DERPServerConfig": { + "type": "object", + "properties": { + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "region_code": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "region_id": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-int" + }, + "region_name": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "relay_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "stun_addresses": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + } + } + }, + "codersdk.DeploymentConfig": { + "type": "object", + "properties": { + "access_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "address": { + "description": "DEPRECATED: Use HTTPAddress or TLS.Address instead.", + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "agent_fallback_troubleshooting_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "agent_stat_refresh_interval": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" + }, + "api_rate_limit": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-int" + }, + "audit_logging": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "auto_import_templates": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "autobuild_poll_interval": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" + }, + "browser_only": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "cache_directory": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "derp": { + "$ref": "#/definitions/codersdk.DERP" + }, + "experimental": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "gitauth": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_codersdk_GitAuthConfig" + }, + "http_address": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "in_memory_database": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "max_token_lifetime": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" + }, + "metrics_cache_refresh_interval": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" + }, + "oauth2": { + "$ref": "#/definitions/codersdk.OAuth2Config" + }, + "oidc": { + "$ref": "#/definitions/codersdk.OIDCConfig" + }, + "pg_connection_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "pprof": { + "$ref": "#/definitions/codersdk.PprofConfig" + }, + "prometheus": { + "$ref": "#/definitions/codersdk.PrometheusConfig" + }, + "provisioner": { + "$ref": "#/definitions/codersdk.ProvisionerConfig" + }, + "proxy_trusted_headers": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "proxy_trusted_origins": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "scim_api_key": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "secure_auth_cookie": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "ssh_keygen_algorithm": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "swagger": { + "$ref": "#/definitions/codersdk.SwaggerConfig" + }, + "telemetry": { + "$ref": "#/definitions/codersdk.TelemetryConfig" + }, + "tls": { + "$ref": "#/definitions/codersdk.TLSConfig" + }, + "trace": { + "$ref": "#/definitions/codersdk.TraceConfig" + }, + "update_check": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "wildcard_access_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + } + } + }, + "codersdk.DeploymentConfigField-array_codersdk_GitAuthConfig": { + "type": "object", + "properties": { + "default": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.GitAuthConfig" + } + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/codersdk.GitAuthConfig" + } + } + }, + "codersdk.DeploymentConfigField-array_string": { + "type": "object", + "properties": { + "default": { + "type": "array", + "items": { + "type": "string" + } + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "codersdk.DeploymentConfigField-bool": { + "type": "object", + "properties": { + "default": { + "type": "boolean" + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "type": "boolean" + } + } + }, + "codersdk.DeploymentConfigField-int": { + "type": "object", + "properties": { + "default": { + "type": "integer" + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "type": "integer" + } + } + }, + "codersdk.DeploymentConfigField-string": { + "type": "object", + "properties": { + "default": { + "type": "string" + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "codersdk.DeploymentConfigField-time_Duration": { + "type": "object", + "properties": { + "default": { + "type": "integer" + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "type": "integer" + } + } + }, + "codersdk.GetAppHostResponse": { + "type": "object", + "properties": { + "host": { + "description": "Host is the externally accessible URL for the Coder instance.", + "type": "string" + } + } + }, + "codersdk.GitAuthConfig": { "type": "object", "properties": { - "organization_id": { - "description": "OrganizationID (optional) adds the set constraint to all resources owned by a given organization.", + "auth_url": { "type": "string" }, - "owner_id": { - "description": "OwnerID (optional) adds the set constraint to all resources owned by a given user.", + "client_id": { "type": "string" }, - "resource_id": { - "description": "ResourceID (optional) reduces the set to a singular resource. This assigns\na resource ID to the resource type, eg: a single workspace.\nThe rbac library will not fetch the resource from the database, so if you\nare using this option, you should also set the owner ID and organization ID\nif possible. Be as specific as possible using all the fields relevant.", + "id": { "type": "string" }, - "resource_type": { - "description": "ResourceType is the name of the resource.\n` + "`" + `./coderd/rbac/object.go` + "`" + ` has the list of valid resource types.", + "no_refresh": { + "type": "boolean" + }, + "regex": { + "type": "string" + }, + "scopes": { + "type": "array", + "items": { + "type": "string" + } + }, + "token_url": { + "type": "string" + }, + "type": { + "type": "string" + }, + "validate_url": { "type": "string" } } }, - "codersdk.AuthorizationRequest": { + "codersdk.Healthcheck": { "type": "object", "properties": { - "checks": { - "description": "Checks is a map keyed with an arbitrary string to a permission check.\nThe key can be any string that is helpful to the caller, and allows\nmultiple permission checks to be run in a single request.\nThe key ensures that each permission check has the same key in the\nresponse.", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/codersdk.AuthorizationCheck" - } + "interval": { + "description": "Interval specifies the seconds between each health check.", + "type": "integer" + }, + "threshold": { + "description": "Threshold specifies the number of consecutive failed health checks before returning \"unhealthy\".", + "type": "integer" + }, + "url": { + "description": "URL specifies the endpoint to check for the app health.", + "type": "string" } } }, - "codersdk.AuthorizationResponse": { + "codersdk.OAuth2Config": { "type": "object", - "additionalProperties": { - "type": "boolean" + "properties": { + "github": { + "$ref": "#/definitions/codersdk.OAuth2GithubConfig" + } } }, - "codersdk.BuildInfoResponse": { + "codersdk.OAuth2GithubConfig": { "type": "object", "properties": { - "external_url": { - "description": "ExternalURL references the current Coder version.\nFor production builds, this will link directly to a release. For development builds, this will link to a commit.", - "type": "string" + "allow_everyone": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" }, - "version": { - "description": "Version returns the semantic version of the build.", - "type": "string" + "allow_signups": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "allowed_orgs": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "allowed_teams": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "client_id": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "client_secret": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "enterprise_base_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" } } }, - "codersdk.CreateParameterRequest": { - "description": "CreateParameterRequest is a structure used to create a new parameter value for a scope.", + "codersdk.OIDCConfig": { "type": "object", - "required": [ - "destination_scheme", - "name", - "source_scheme", - "source_value" - ], "properties": { - "copy_from_parameter": { - "description": "CloneID allows copying the value of another parameter.\nThe other param must be related to the same template_id for this to\nsucceed.\nNo other fields are required if using this, as all fields will be copied\nfrom the other parameter.", + "allow_signups": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "client_id": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "client_secret": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "email_domain": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "ignore_email_verified": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "issuer_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "scopes": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + } + } + }, + "codersdk.Parameter": { + "description": "Parameter represents a set value for the scope.", + "type": "object", + "properties": { + "created_at": { "type": "string" }, "destination_scheme": { "type": "string", "enum": [ + "none", "environment_variable", "provisioner_variable" ] }, + "id": { + "type": "string", + "format": "uuid" + }, "name": { "type": "string" }, + "scope": { + "type": "string", + "enum": [ + "template", + "workspace", + "import_job" + ] + }, + "scope_id": { + "type": "string", + "format": "uuid" + }, "source_scheme": { "type": "string", "enum": [ + "none", "data" ] }, - "source_value": { + "updated_at": { "type": "string" } } }, - "codersdk.CreateTemplateRequest": { + "codersdk.PprofConfig": { "type": "object", - "required": [ - "name", - "template_version_id" - ], "properties": { - "allow_user_cancel_workspace_jobs": { - "description": "Allow users to cancel in-progress workspace jobs.\n*bool as the default value is \"true\".", - "type": "boolean" - }, - "default_ttl_ms": { - "description": "DefaultTTLMillis allows optionally specifying the default TTL\nfor all workspaces created from this template.", - "type": "integer" - }, - "description": { - "description": "Description is a description of what the template contains. It must be\nless than 128 bytes.", - "type": "string" - }, - "display_name": { - "description": "DisplayName is the displayed name of the template.", - "type": "string" - }, - "icon": { - "description": "Icon is a relative path or external URL that specifies\nan icon to be displayed in the dashboard.", - "type": "string" - }, - "name": { - "description": "Name is the name of the template.", - "type": "string" - }, - "parameter_values": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.CreateParameterRequest" - } + "address": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" }, - "template_version_id": { - "description": "VersionID is an in-progress or completed job to use as an initial version\nof the template.\n\nThis is required on creation to enable a user-flow of validating a\ntemplate works. There is no reason the data-model cannot support empty\ntemplates, but it doesn't make sense for users.", - "type": "string" + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" } } }, - "codersdk.DERPRegion": { + "codersdk.PrometheusConfig": { "type": "object", "properties": { - "latency_ms": { - "type": "number" + "address": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" }, - "preferred": { - "type": "boolean" + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" } } }, - "codersdk.GetAppHostResponse": { - "type": "object", - "properties": { - "host": { - "description": "Host is the externally accessible URL for the Coder instance.", - "type": "string" - } - } - }, - "codersdk.Healthcheck": { + "codersdk.ProvisionerConfig": { "type": "object", "properties": { - "interval": { - "description": "Interval specifies the seconds between each health check.", - "type": "integer" + "daemon_poll_interval": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" }, - "threshold": { - "description": "Threshold specifies the number of consecutive failed health checks before returning \"unhealthy\".", - "type": "integer" + "daemon_poll_jitter": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" }, - "url": { - "description": "URL specifies the endpoint to check for the app health.", - "type": "string" - } - } - }, - "codersdk.NullTime": { - "type": "object", - "properties": { - "time": { - "type": "string" + "daemons": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-int" }, - "valid": { - "description": "Valid is true if Time is not NULL", - "type": "boolean" + "force_cancel_interval": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" } } }, @@ -1103,6 +2132,74 @@ const docTemplate = `{ } } }, + "codersdk.Role": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "codersdk.SwaggerConfig": { + "type": "object", + "properties": { + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + } + } + }, + "codersdk.TLSConfig": { + "type": "object", + "properties": { + "address": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "cert_file": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "client_auth": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "client_ca_file": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "client_cert_file": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "client_key_file": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "key_file": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "min_version": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "redirect_http": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + } + } + }, + "codersdk.TelemetryConfig": { + "type": "object", + "properties": { + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "trace": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + } + } + }, "codersdk.Template": { "type": "object", "properties": { @@ -1171,6 +2268,20 @@ const docTemplate = `{ "$ref": "#/definitions/codersdk.TransitionStats" } }, + "codersdk.TraceConfig": { + "type": "object", + "properties": { + "capture_logs": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "honeycomb_api_key": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + } + } + }, "codersdk.TransitionStats": { "type": "object", "properties": { @@ -1225,6 +2336,58 @@ const docTemplate = `{ } } }, + "codersdk.UploadResponse": { + "type": "object", + "properties": { + "hash": { + "type": "string" + } + } + }, + "codersdk.User": { + "type": "object", + "required": [ + "created_at", + "email", + "id", + "username" + ], + "properties": { + "avatar_url": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "last_seen_at": { + "type": "string" + }, + "organization_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Role" + } + }, + "status": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "codersdk.ValidationError": { "type": "object", "required": [ @@ -1433,7 +2596,8 @@ const docTemplate = `{ "type": "integer" }, "deadline": { - "$ref": "#/definitions/codersdk.NullTime" + "type": "string", + "format": "date-time" }, "id": { "type": "string", @@ -1587,6 +2751,9 @@ const docTemplate = `{ } } } + }, + "netip.Addr": { + "type": "object" } }, "securityDefinitions": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index f33bec806f70a..ef30670b3a406 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -79,6 +79,84 @@ } } }, + "/audit": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Audit"], + "summary": "Get audit logs", + "operationId": "get-audit-logs", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "q", + "in": "query", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "After ID", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AuditLogResponse" + } + } + } + } + }, + "/audit/testgenerate": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "tags": ["Audit"], + "summary": "Generate fake audit log", + "operationId": "generate-fake-audit-logs", + "parameters": [ + { + "description": "Audit log request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTestAuditLogRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, "/authcheck": { "post": { "security": [ @@ -128,6 +206,27 @@ } } }, + "/config/deployment": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["General"], + "summary": "Get deployment config", + "operationId": "get-deployment-config", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.DeploymentConfig" + } + } + } + } + }, "/csp/reports": { "post": { "security": [ @@ -158,6 +257,73 @@ } } }, + "/files": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "description": "Notice: Swagger 2.0 doesn't support file upload with a `content-type` different than `application/x-www-form-urlencoded`.", + "consumes": ["application/x-tar"], + "produces": ["application/json"], + "tags": ["Files"], + "summary": "Upload file", + "operationId": "update-file", + "parameters": [ + { + "type": "string", + "default": "application/x-tar", + "description": "Content-Type must be `application/x-tar`", + "name": "Content-Type", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "File to be uploaded", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.UploadResponse" + } + } + } + } + }, + "/files/{fileID}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "tags": ["Files"], + "summary": "Get file by ID", + "operationId": "get-file-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "File ID", + "name": "fileID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/organizations/{organization-id}/templates/": { "post": { "security": [ @@ -308,6 +474,141 @@ } } }, + "/parameters/{scope}/{id}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Parameters"], + "summary": "Get parameters", + "operationId": "get-parameters", + "parameters": [ + { + "enum": ["template", "workspace", "import_job"], + "type": "string", + "description": "Scope", + "name": "scope", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Parameter" + } + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Parameters"], + "summary": "Create parameter", + "operationId": "create-parameter", + "parameters": [ + { + "description": "Parameter request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateParameterRequest" + } + }, + { + "enum": ["template", "workspace", "import_job"], + "type": "string", + "description": "Scope", + "name": "scope", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.Parameter" + } + } + } + } + }, + "/parameters/{scope}/{id}/{name}": { + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Parameters"], + "summary": "Delete parameter", + "operationId": "delete-parameter", + "parameters": [ + { + "enum": ["template", "workspace", "import_job"], + "type": "string", + "description": "Scope", + "name": "scope", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, "/templates/{id}": { "get": { "security": [ @@ -317,8 +618,8 @@ ], "produces": ["application/json"], "tags": ["Templates"], - "summary": "Update template metadata by ID", - "operationId": "update-template-metadata", + "summary": "Get template metadata by ID", + "operationId": "get-template-metadata-by-id", "parameters": [ { "type": "string", @@ -366,6 +667,35 @@ } } } + }, + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Update template metadata by ID", + "operationId": "update-template-metadata", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Template" + } + } + } } }, "/updatecheck": { @@ -726,10 +1056,103 @@ } } }, - "codersdk.AuthorizationCheck": { - "description": "AuthorizationCheck is used to check if the currently authenticated user (or the specified user) can do a given action to a given set of objects.", + "codersdk.AuditDiff": { "type": "object", - "properties": { + "additionalProperties": { + "$ref": "#/definitions/codersdk.AuditDiffField" + } + }, + "codersdk.AuditDiffField": { + "type": "object", + "properties": { + "new": {}, + "old": {}, + "secret": { + "type": "boolean" + } + } + }, + "codersdk.AuditLog": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "additional_fields": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": { + "type": "string" + }, + "diff": { + "$ref": "#/definitions/codersdk.AuditDiff" + }, + "id": { + "type": "string" + }, + "ip": { + "$ref": "#/definitions/netip.Addr" + }, + "is_deleted": { + "type": "boolean" + }, + "organization_id": { + "type": "string" + }, + "request_id": { + "type": "string" + }, + "resource_icon": { + "type": "string" + }, + "resource_id": { + "type": "string" + }, + "resource_link": { + "type": "string" + }, + "resource_target": { + "description": "ResourceTarget is the name of the resource.", + "type": "string" + }, + "resource_type": { + "type": "string" + }, + "status_code": { + "type": "integer" + }, + "time": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/codersdk.User" + }, + "user_agent": { + "type": "string" + } + } + }, + "codersdk.AuditLogResponse": { + "type": "object", + "properties": { + "audit_logs": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.AuditLog" + } + }, + "count": { + "type": "integer" + } + } + }, + "codersdk.AuthorizationCheck": { + "description": "AuthorizationCheck is used to check if the currently authenticated user (or the specified user) can do a given action to a given set of objects.", + "type": "object", + "properties": { "action": { "type": "string", "enum": ["create", "read", "update", "delete"] @@ -809,14 +1232,14 @@ }, "destination_scheme": { "type": "string", - "enum": ["environment_variable", "provisioner_variable"] + "enum": ["none", "environment_variable", "provisioner_variable"] }, "name": { "type": "string" }, "source_scheme": { "type": "string", - "enum": ["data"] + "enum": ["none", "data"] }, "source_value": { "type": "string" @@ -863,6 +1286,57 @@ } } }, + "codersdk.CreateTestAuditLogRequest": { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": ["create", "write", "delete", "start", "stop"] + }, + "resource_id": { + "type": "string" + }, + "resource_type": { + "type": "string", + "enum": [ + "organization", + "template", + "template_version", + "user", + "workspace", + "workspace_build", + "git_ssh_key", + "api_key", + "group" + ] + }, + "time": { + "type": "string" + } + } + }, + "codersdk.DERP": { + "type": "object", + "properties": { + "config": { + "$ref": "#/definitions/codersdk.DERPConfig" + }, + "server": { + "$ref": "#/definitions/codersdk.DERPServerConfig" + } + } + }, + "codersdk.DERPConfig": { + "type": "object", + "properties": { + "path": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + } + } + }, "codersdk.DERPRegion": { "type": "object", "properties": { @@ -874,6 +1348,335 @@ } } }, + "codersdk.DERPServerConfig": { + "type": "object", + "properties": { + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "region_code": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "region_id": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-int" + }, + "region_name": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "relay_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "stun_addresses": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + } + } + }, + "codersdk.DeploymentConfig": { + "type": "object", + "properties": { + "access_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "address": { + "description": "DEPRECATED: Use HTTPAddress or TLS.Address instead.", + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "agent_fallback_troubleshooting_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "agent_stat_refresh_interval": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" + }, + "api_rate_limit": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-int" + }, + "audit_logging": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "auto_import_templates": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "autobuild_poll_interval": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" + }, + "browser_only": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "cache_directory": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "derp": { + "$ref": "#/definitions/codersdk.DERP" + }, + "experimental": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "gitauth": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_codersdk_GitAuthConfig" + }, + "http_address": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "in_memory_database": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "max_token_lifetime": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" + }, + "metrics_cache_refresh_interval": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" + }, + "oauth2": { + "$ref": "#/definitions/codersdk.OAuth2Config" + }, + "oidc": { + "$ref": "#/definitions/codersdk.OIDCConfig" + }, + "pg_connection_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "pprof": { + "$ref": "#/definitions/codersdk.PprofConfig" + }, + "prometheus": { + "$ref": "#/definitions/codersdk.PrometheusConfig" + }, + "provisioner": { + "$ref": "#/definitions/codersdk.ProvisionerConfig" + }, + "proxy_trusted_headers": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "proxy_trusted_origins": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "scim_api_key": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "secure_auth_cookie": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "ssh_keygen_algorithm": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "swagger": { + "$ref": "#/definitions/codersdk.SwaggerConfig" + }, + "telemetry": { + "$ref": "#/definitions/codersdk.TelemetryConfig" + }, + "tls": { + "$ref": "#/definitions/codersdk.TLSConfig" + }, + "trace": { + "$ref": "#/definitions/codersdk.TraceConfig" + }, + "update_check": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "wildcard_access_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + } + } + }, + "codersdk.DeploymentConfigField-array_codersdk_GitAuthConfig": { + "type": "object", + "properties": { + "default": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.GitAuthConfig" + } + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/codersdk.GitAuthConfig" + } + } + }, + "codersdk.DeploymentConfigField-array_string": { + "type": "object", + "properties": { + "default": { + "type": "array", + "items": { + "type": "string" + } + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "codersdk.DeploymentConfigField-bool": { + "type": "object", + "properties": { + "default": { + "type": "boolean" + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "type": "boolean" + } + } + }, + "codersdk.DeploymentConfigField-int": { + "type": "object", + "properties": { + "default": { + "type": "integer" + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "type": "integer" + } + } + }, + "codersdk.DeploymentConfigField-string": { + "type": "object", + "properties": { + "default": { + "type": "string" + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "codersdk.DeploymentConfigField-time_Duration": { + "type": "object", + "properties": { + "default": { + "type": "integer" + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "type": "integer" + } + } + }, "codersdk.GetAppHostResponse": { "type": "object", "properties": { @@ -883,6 +1686,41 @@ } } }, + "codersdk.GitAuthConfig": { + "type": "object", + "properties": { + "auth_url": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "no_refresh": { + "type": "boolean" + }, + "regex": { + "type": "string" + }, + "scopes": { + "type": "array", + "items": { + "type": "string" + } + }, + "token_url": { + "type": "string" + }, + "type": { + "type": "string" + }, + "validate_url": { + "type": "string" + } + } + }, "codersdk.Healthcheck": { "type": "object", "properties": { @@ -900,15 +1738,137 @@ } } }, - "codersdk.NullTime": { + "codersdk.OAuth2Config": { "type": "object", "properties": { - "time": { + "github": { + "$ref": "#/definitions/codersdk.OAuth2GithubConfig" + } + } + }, + "codersdk.OAuth2GithubConfig": { + "type": "object", + "properties": { + "allow_everyone": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "allow_signups": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "allowed_orgs": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "allowed_teams": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "client_id": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "client_secret": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "enterprise_base_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + } + } + }, + "codersdk.OIDCConfig": { + "type": "object", + "properties": { + "allow_signups": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "client_id": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "client_secret": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "email_domain": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "ignore_email_verified": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "issuer_url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "scopes": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + } + } + }, + "codersdk.Parameter": { + "description": "Parameter represents a set value for the scope.", + "type": "object", + "properties": { + "created_at": { "type": "string" }, - "valid": { - "description": "Valid is true if Time is not NULL", - "type": "boolean" + "destination_scheme": { + "type": "string", + "enum": ["none", "environment_variable", "provisioner_variable"] + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "scope": { + "type": "string", + "enum": ["template", "workspace", "import_job"] + }, + "scope_id": { + "type": "string", + "format": "uuid" + }, + "source_scheme": { + "type": "string", + "enum": ["none", "data"] + }, + "updated_at": { + "type": "string" + } + } + }, + "codersdk.PprofConfig": { + "type": "object", + "properties": { + "address": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + } + } + }, + "codersdk.PrometheusConfig": { + "type": "object", + "properties": { + "address": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + } + } + }, + "codersdk.ProvisionerConfig": { + "type": "object", + "properties": { + "daemon_poll_interval": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" + }, + "daemon_poll_jitter": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" + }, + "daemons": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-int" + }, + "force_cancel_interval": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-time_Duration" } } }, @@ -979,6 +1939,74 @@ } } }, + "codersdk.Role": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "codersdk.SwaggerConfig": { + "type": "object", + "properties": { + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + } + } + }, + "codersdk.TLSConfig": { + "type": "object", + "properties": { + "address": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "cert_file": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "client_auth": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "client_ca_file": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "client_cert_file": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "client_key_file": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "key_file": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" + }, + "min_version": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + }, + "redirect_http": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + } + } + }, + "codersdk.TelemetryConfig": { + "type": "object", + "properties": { + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "trace": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "url": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + } + } + }, "codersdk.Template": { "type": "object", "properties": { @@ -1047,6 +2075,20 @@ "$ref": "#/definitions/codersdk.TransitionStats" } }, + "codersdk.TraceConfig": { + "type": "object", + "properties": { + "capture_logs": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "enable": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-bool" + }, + "honeycomb_api_key": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-string" + } + } + }, "codersdk.TransitionStats": { "type": "object", "properties": { @@ -1101,6 +2143,53 @@ } } }, + "codersdk.UploadResponse": { + "type": "object", + "properties": { + "hash": { + "type": "string" + } + } + }, + "codersdk.User": { + "type": "object", + "required": ["created_at", "email", "id", "username"], + "properties": { + "avatar_url": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "last_seen_at": { + "type": "string" + }, + "organization_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Role" + } + }, + "status": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "codersdk.ValidationError": { "type": "object", "required": ["detail", "field"], @@ -1306,7 +2395,8 @@ "type": "integer" }, "deadline": { - "$ref": "#/definitions/codersdk.NullTime" + "type": "string", + "format": "date-time" }, "id": { "type": "string", @@ -1452,6 +2542,9 @@ } } } + }, + "netip.Addr": { + "type": "object" } }, "securityDefinitions": { diff --git a/coderd/audit.go b/coderd/audit.go index eaac13a66cfbb..72b27690fb179 100644 --- a/coderd/audit.go +++ b/coderd/audit.go @@ -24,6 +24,17 @@ import ( "github.com/coder/coder/codersdk" ) +// @Summary Get audit logs +// @ID get-audit-logs +// @Security CoderSessionToken +// @Produce json +// @Tags Audit +// @Param q query string true "Search query" +// @Param after_id query string false "After ID" format(uuid) +// @Param limit query int false "Page limit" +// @Param offset query int false "Page offset" +// @Success 200 {object} codersdk.AuditLogResponse +// @Router /audit [get] func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() if !api.Authorize(r, rbac.ActionRead, rbac.ResourceAuditLog) { @@ -77,6 +88,14 @@ func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) { }) } +// @Summary Generate fake audit log +// @ID generate-fake-audit-logs +// @Security CoderSessionToken +// @Accept json +// @Tags Audit +// @Param request body codersdk.CreateTestAuditLogRequest true "Audit log request" +// @Success 204 +// @Router /audit/testgenerate [post] func (api *API) generateFakeAuditLog(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() if !api.Authorize(r, rbac.ActionCreate, rbac.ResourceAuditLog) { diff --git a/coderd/deploymentconfig.go b/coderd/deploymentconfig.go index d68332c9089f7..9b84120d77af5 100644 --- a/coderd/deploymentconfig.go +++ b/coderd/deploymentconfig.go @@ -7,6 +7,13 @@ import ( "github.com/coder/coder/coderd/rbac" ) +// @Summary Get deployment config +// @ID get-deployment-config +// @Security CoderSessionToken +// @Produce json +// @Tags General +// @Success 200 {object} codersdk.DeploymentConfig +// @Router /config/deployment [get] func (api *API) deploymentConfig(rw http.ResponseWriter, r *http.Request) { if !api.Authorize(r, rbac.ActionRead, rbac.ResourceDeploymentConfig) { httpapi.Forbidden(rw) diff --git a/coderd/files.go b/coderd/files.go index 8d01745f919c6..5178f61a9d00d 100644 --- a/coderd/files.go +++ b/coderd/files.go @@ -23,6 +23,17 @@ const ( tarMimeType = "application/x-tar" ) +// @Summary Upload file +// @Description Notice: Swagger 2.0 doesn't support file upload with a `content-type` different than `application/x-www-form-urlencoded`. +// @ID update-file +// @Security CoderSessionToken +// @Produce json +// @Accept application/x-tar +// @Tags Files +// @Param Content-Type header string true "Content-Type must be `application/x-tar`" default(application/x-tar) +// @Param file formData file true "File to be uploaded" +// @Success 201 {object} codersdk.UploadResponse +// @Router /files [post] func (api *API) postFile(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -88,6 +99,13 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) { }) } +// @Summary Get file by ID +// @ID get-file-by-id +// @Security CoderSessionToken +// @Tags Files +// @Param fileID path string true "File ID" format(uuid) +// @Success 200 +// @Router /files/{fileID} [get] func (api *API) fileByID(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/coderd/parameters.go b/coderd/parameters.go index ba3fd2349f48a..29adb4bf5e43b 100644 --- a/coderd/parameters.go +++ b/coderd/parameters.go @@ -18,6 +18,17 @@ import ( "github.com/coder/coder/codersdk" ) +// @Summary Create parameter +// @ID create-parameter +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Parameters +// @Param request body codersdk.CreateParameterRequest true "Parameter request" +// @Param scope path string true "Scope" Enums(template,workspace,import_job) +// @Param id path string true "ID" format(uuid) +// @Success 201 {object} codersdk.Parameter +// @Router /parameters/{scope}/{id} [post] func (api *API) postParameter(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() scope, scopeID, valid := readScopeAndID(ctx, rw, r) @@ -78,6 +89,15 @@ func (api *API) postParameter(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusCreated, convertParameterValue(parameterValue)) } +// @Summary Get parameters +// @ID get-parameters +// @Security CoderSessionToken +// @Produce json +// @Tags Parameters +// @Param scope path string true "Scope" Enums(template,workspace,import_job) +// @Param id path string true "ID" format(uuid) +// @Success 200 {array} codersdk.Parameter +// @Router /parameters/{scope}/{id} [get] func (api *API) parameters(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() scope, scopeID, valid := readScopeAndID(ctx, rw, r) @@ -116,6 +136,16 @@ func (api *API) parameters(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, apiParameterValues) } +// @Summary Delete parameter +// @ID delete-parameter +// @Security CoderSessionToken +// @Produce json +// @Tags Parameters +// @Param scope path string true "Scope" Enums(template,workspace,import_job) +// @Param id path string true "ID" format(uuid) +// @Param name path string true "Name" +// @Success 200 {object} codersdk.Response +// @Router /parameters/{scope}/{id}/{name} [delete] func (api *API) deleteParameter(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() scope, scopeID, valid := readScopeAndID(ctx, rw, r) diff --git a/coderd/templates.go b/coderd/templates.go index 74e3560703131..662a3069683c7 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -478,7 +478,7 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re // @Tags Templates // @Param id path string true "Template ID" format(uuid) // @Success 200 {object} codersdk.Template -// @Router /templates/{id} [get] +// @Router /templates/{id} [patch] func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/codersdk/audit.go b/codersdk/audit.go index e3d25851ea89f..da8f88df04ade 100644 --- a/codersdk/audit.go +++ b/codersdk/audit.go @@ -121,8 +121,8 @@ type AuditLogResponse struct { } type CreateTestAuditLogRequest struct { - Action AuditAction `json:"action,omitempty"` - ResourceType ResourceType `json:"resource_type,omitempty"` + Action AuditAction `json:"action,omitempty" enums:"create,write,delete,start,stop"` + ResourceType ResourceType `json:"resource_type,omitempty" enums:"organization,template,template_version,user,workspace,workspace_build,git_ssh_key,api_key,group"` ResourceID uuid.UUID `json:"resource_id,omitempty"` Time time.Time `json:"time,omitempty"` } diff --git a/codersdk/parameters.go b/codersdk/parameters.go index c813096412a11..92717e55e1651 100644 --- a/codersdk/parameters.go +++ b/codersdk/parameters.go @@ -49,13 +49,15 @@ type ComputedParameter struct { } // Parameter represents a set value for the scope. +// +// @Description Parameter represents a set value for the scope. type Parameter struct { - ID uuid.UUID `json:"id" table:"id"` - Scope ParameterScope `json:"scope" table:"scope"` - ScopeID uuid.UUID `json:"scope_id" table:"scope id"` + ID uuid.UUID `json:"id" table:"id" format:"uuid"` + Scope ParameterScope `json:"scope" table:"scope" enums:"template,workspace,import_job"` + ScopeID uuid.UUID `json:"scope_id" table:"scope id" format:"uuid"` Name string `json:"name" table:"name"` - SourceScheme ParameterSourceScheme `json:"source_scheme" table:"source scheme" validate:"ne=none"` - DestinationScheme ParameterDestinationScheme `json:"destination_scheme" table:"destination scheme" validate:"ne=none"` + SourceScheme ParameterSourceScheme `json:"source_scheme" table:"source scheme" validate:"ne=none" enums:"none,data"` + DestinationScheme ParameterDestinationScheme `json:"destination_scheme" table:"destination scheme" validate:"ne=none" enums:"none,environment_variable,provisioner_variable"` CreatedAt time.Time `json:"created_at" table:"created at"` UpdatedAt time.Time `json:"updated_at" table:"updated at"` } @@ -96,8 +98,8 @@ type CreateParameterRequest struct { Name string `json:"name" validate:"required"` SourceValue string `json:"source_value" validate:"required"` - SourceScheme ParameterSourceScheme `json:"source_scheme" validate:"oneof=data,required"` - DestinationScheme ParameterDestinationScheme `json:"destination_scheme" validate:"oneof=environment_variable provisioner_variable,required"` + SourceScheme ParameterSourceScheme `json:"source_scheme" validate:"oneof=data,required" enums:"none,data"` + DestinationScheme ParameterDestinationScheme `json:"destination_scheme" validate:"oneof=environment_variable provisioner_variable,required" enums:"none,environment_variable,provisioner_variable"` } func (c *Client) CreateParameter(ctx context.Context, scope ParameterScope, id uuid.UUID, req CreateParameterRequest) (Parameter, error) { diff --git a/codersdk/workspacebuilds.go b/codersdk/workspacebuilds.go index 80055484df0f6..5281710a797b0 100644 --- a/codersdk/workspacebuilds.go +++ b/codersdk/workspacebuilds.go @@ -67,7 +67,7 @@ type WorkspaceBuild struct { Job ProvisionerJob `json:"job"` Reason BuildReason `db:"reason" json:"reason"` Resources []WorkspaceResource `json:"resources"` - Deadline NullTime `json:"deadline,omitempty"` + Deadline NullTime `json:"deadline,omitempty" swaggertype:"string" format:"date-time"` Status WorkspaceStatus `json:"status" enums:"pending,starting,running,stopping,stopped,failed,canceling,canceled,deleting,deleted"` DailyCost int32 `json:"daily_cost"` } diff --git a/docs/api/audit.md b/docs/api/audit.md new file mode 100644 index 0000000000000..8b3946c715665 --- /dev/null +++ b/docs/api/audit.md @@ -0,0 +1,129 @@ +# Audit + +> This page is incomplete, stay tuned. + +## Get audit logs + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/audit?q=string \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /audit` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------- | ----- | ------------ | -------- | ------------ | +| `q` | query | string | true | Search query | +| `after_id` | query | string(uuid) | false | After ID | +| `limit` | query | integer | false | Page limit | +| `offset` | query | integer | false | Page offset | + +### Example responses + +> 200 Response + +```json +{ + "audit_logs": [ + { + "action": "string", + "additional_fields": [0], + "description": "string", + "diff": { + "property1": { + "new": null, + "old": null, + "secret": true + }, + "property2": { + "new": null, + "old": null, + "secret": true + } + }, + "id": "string", + "ip": {}, + "is_deleted": true, + "organization_id": "string", + "request_id": "string", + "resource_icon": "string", + "resource_id": "string", + "resource_link": "string", + "resource_target": "string", + "resource_type": "string", + "status_code": 0, + "time": "string", + "user": { + "avatar_url": "string", + "created_at": "string", + "email": "string", + "id": "string", + "last_seen_at": "string", + "organization_ids": ["string"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "string", + "username": "string" + }, + "user_agent": "string" + } + ], + "count": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.AuditLogResponse](schemas.md#codersdkauditlogresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Generate fake audit log + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/audit/testgenerate \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /audit/testgenerate` + +> Body parameter + +```json +{ + "action": "create", + "resource_id": "string", + "resource_type": "organization", + "time": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ---------------------------------------------------------------------------------- | -------- | ----------------- | +| `body` | body | [codersdk.CreateTestAuditLogRequest](schemas.md#codersdkcreatetestauditlogrequest) | true | Audit log request | + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | --------------------------------------------------------------- | ----------- | ------ | +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/api/files.md b/docs/api/files.md new file mode 100644 index 0000000000000..85cb46bcdf74b --- /dev/null +++ b/docs/api/files.md @@ -0,0 +1,77 @@ +# Files + +> This page is incomplete, stay tuned. + +## Upload file + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/files \ + -H 'Accept: application/json' \ + -H 'Content-Type: application/x-tar' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /files` + +Notice: Swagger 2.0 doesn't support file upload with a `content-type` different than `application/x-www-form-urlencoded`. + +> Body parameter + +```yaml +file: string +``` + +### Parameters + +| Name | In | Type | Required | Description | +| -------------- | ------ | -------------- | -------- | ---------------------------------------- | +| `Content-Type` | header | string | true | Content-Type must be `application/x-tar` | +| `body` | body | object | true | | +| `» file` | body | string(binary) | true | File to be uploaded | + +### Example responses + +> 201 Response + +```json +{ + "hash": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------ | ----------- | ------------------------------------------------------------ | +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.UploadResponse](schemas.md#codersdkuploadresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get file by ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/files/{fileID} \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /files/{fileID}` + +### Parameters + +| Name | In | Type | Required | Description | +| -------- | ---- | ------------ | -------- | ----------- | +| `fileID` | path | string(uuid) | true | File ID | + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/api/general.md b/docs/api/general.md index 8d5ef5dad967d..f8293d32c8709 100644 --- a/docs/api/general.md +++ b/docs/api/general.md @@ -37,8 +37,6 @@ curl -X GET http://coder-server:8080/api/v2/ \ | ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | -undefined - ## Build info ### Code samples @@ -68,7 +66,864 @@ curl -X GET http://coder-server:8080/api/v2/buildinfo \ | ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------------ | | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.BuildInfoResponse](schemas.md#codersdkbuildinforesponse) | -undefined +## Get deployment config + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/config/deployment \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /config/deployment` + +### Example responses + +> 200 Response + +```json +{ + "access_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "agent_fallback_troubleshooting_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "agent_stat_refresh_interval": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "api_rate_limit": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "audit_logging": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "auto_import_templates": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "autobuild_poll_interval": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "browser_only": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "cache_directory": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "derp": { + "config": { + "path": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + }, + "server": { + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "region_code": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "region_id": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "region_name": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "relay_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "stun_addresses": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + } + }, + "experimental": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "gitauth": { + "default": [ + { + "auth_url": "string", + "client_id": "string", + "id": "string", + "no_refresh": true, + "regex": "string", + "scopes": ["string"], + "token_url": "string", + "type": "string", + "validate_url": "string" + } + ], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": { + "auth_url": "string", + "client_id": "string", + "id": "string", + "no_refresh": true, + "regex": "string", + "scopes": ["string"], + "token_url": "string", + "type": "string", + "validate_url": "string" + } + }, + "http_address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "in_memory_database": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "max_token_lifetime": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "metrics_cache_refresh_interval": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "oauth2": { + "github": { + "allow_everyone": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "allow_signups": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "allowed_orgs": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "allowed_teams": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_id": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_secret": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enterprise_base_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + } + }, + "oidc": { + "allow_signups": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "client_id": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_secret": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "email_domain": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "ignore_email_verified": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "issuer_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "scopes": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + }, + "pg_connection_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "pprof": { + "address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + } + }, + "prometheus": { + "address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + } + }, + "provisioner": { + "daemon_poll_interval": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "daemon_poll_jitter": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "daemons": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "force_cancel_interval": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + } + }, + "proxy_trusted_headers": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "proxy_trusted_origins": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "scim_api_key": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "secure_auth_cookie": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "ssh_keygen_algorithm": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "swagger": { + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + } + }, + "telemetry": { + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "trace": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + }, + "tls": { + "address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "cert_file": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_auth": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_ca_file": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_cert_file": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_key_file": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "key_file": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "min_version": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "redirect_http": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + } + }, + "trace": { + "capture_logs": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "honeycomb_api_key": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + }, + "update_check": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "wildcard_access_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.DeploymentConfig](schemas.md#codersdkdeploymentconfig) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. ## Report CSP violations @@ -134,5 +989,3 @@ curl -X GET http://coder-server:8080/api/v2/updatecheck \ | Status | Meaning | Description | Schema | | ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------- | | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UpdateCheckResponse](schemas.md#codersdkupdatecheckresponse) | - -undefined diff --git a/docs/api/parameters.md b/docs/api/parameters.md new file mode 100644 index 0000000000000..5b035413f09b0 --- /dev/null +++ b/docs/api/parameters.md @@ -0,0 +1,208 @@ +# Parameters + +> This page is incomplete, stay tuned. + +## Get parameters + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/parameters/{scope}/{id} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /parameters/{scope}/{id}` + +### Parameters + +| Name | In | Type | Required | Description | +| ------- | ---- | ------------ | -------- | ----------- | +| `scope` | path | string | true | Scope | +| `id` | path | string(uuid) | true | ID | + +#### Enumerated Values + +| Parameter | Value | +| --------- | ------------ | +| `scope` | `template` | +| `scope` | `workspace` | +| `scope` | `import_job` | + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "string", + "destination_scheme": "none", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "scope": "template", + "scope_id": "5d3fe357-12dd-4f62-b004-6d1fb3b8454f", + "source_scheme": "none", + "updated_at": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Parameter](schemas.md#codersdkparameter) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| ---------------------- | ------ | -------- | ------------ | ------------------------------------------------- | +| `[array item]` | array | false | | [Parameter represents a set value for the scope.] | +| `» created_at` | string | false | | | +| `» destination_scheme` | string | false | | | +| `» id` | string | false | | | +| `» name` | string | false | | | +| `» scope` | string | false | | | +| `» scope_id` | string | false | | | +| `» source_scheme` | string | false | | | +| `» updated_at` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| -------------------- | ---------------------- | +| `destination_scheme` | `none` | +| `destination_scheme` | `environment_variable` | +| `destination_scheme` | `provisioner_variable` | +| `scope` | `template` | +| `scope` | `workspace` | +| `scope` | `import_job` | +| `source_scheme` | `none` | +| `source_scheme` | `data` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Create parameter + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/parameters/{scope}/{id} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /parameters/{scope}/{id}` + +> Body parameter + +```json +{ + "copy_from_parameter": "string", + "destination_scheme": "none", + "name": "string", + "source_scheme": "none", + "source_value": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------- | ---- | ---------------------------------------------------------------------------- | -------- | ----------------- | +| `scope` | path | string | true | Scope | +| `id` | path | string(uuid) | true | ID | +| `body` | body | [codersdk.CreateParameterRequest](schemas.md#codersdkcreateparameterrequest) | true | Parameter request | + +#### Enumerated Values + +| Parameter | Value | +| --------- | ------------ | +| `scope` | `template` | +| `scope` | `workspace` | +| `scope` | `import_job` | + +### Example responses + +> 201 Response + +```json +{ + "created_at": "string", + "destination_scheme": "none", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "scope": "template", + "scope_id": "5d3fe357-12dd-4f62-b004-6d1fb3b8454f", + "source_scheme": "none", + "updated_at": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------ | ----------- | -------------------------------------------------- | +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.Parameter](schemas.md#codersdkparameter) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Delete parameter + +### Code samples + +```shell +# Example request using curl +curl -X DELETE http://coder-server:8080/api/v2/parameters/{scope}/{id}/{name} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`DELETE /parameters/{scope}/{id}/{name}` + +### Parameters + +| Name | In | Type | Required | Description | +| ------- | ---- | ------------ | -------- | ----------- | +| `scope` | path | string | true | Scope | +| `id` | path | string(uuid) | true | ID | +| `name` | path | string | true | Name | + +#### Enumerated Values + +| Parameter | Value | +| --------- | ------------ | +| `scope` | `template` | +| `scope` | `workspace` | +| `scope` | `import_job` | + +### Example responses + +> 200 Response + +```json +{ + "detail": "string", + "message": "string", + "validations": [ + { + "detail": "string", + "field": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 601a1fc7a396a..369d9d2688cc8 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -16,6 +16,184 @@ | ------------ | ------ | -------- | ------------ | ----------- | | `csp-report` | object | false | | | +## codersdk.AuditDiff + +```json +{ + "property1": { + "new": null, + "old": null, + "secret": true + }, + "property2": { + "new": null, + "old": null, + "secret": true + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------------- | -------------------------------------------------- | -------- | ------------ | ----------- | +| `[any property]` | [codersdk.AuditDiffField](#codersdkauditdifffield) | false | | | + +## codersdk.AuditDiffField + +```json +{ + "new": null, + "old": null, + "secret": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------- | ------- | -------- | ------------ | ----------- | +| `new` | any | false | | | +| `old` | any | false | | | +| `secret` | boolean | false | | | + +## codersdk.AuditLog + +```json +{ + "action": "string", + "additional_fields": [0], + "description": "string", + "diff": { + "property1": { + "new": null, + "old": null, + "secret": true + }, + "property2": { + "new": null, + "old": null, + "secret": true + } + }, + "id": "string", + "ip": {}, + "is_deleted": true, + "organization_id": "string", + "request_id": "string", + "resource_icon": "string", + "resource_id": "string", + "resource_link": "string", + "resource_target": "string", + "resource_type": "string", + "status_code": 0, + "time": "string", + "user": { + "avatar_url": "string", + "created_at": "string", + "email": "string", + "id": "string", + "last_seen_at": "string", + "organization_ids": ["string"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "string", + "username": "string" + }, + "user_agent": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------- | ---------------------------------------- | -------- | ------------ | -------------------------------------------- | +| `action` | string | false | | | +| `additional_fields` | array of integer | false | | | +| `description` | string | false | | | +| `diff` | [codersdk.AuditDiff](#codersdkauditdiff) | false | | | +| `id` | string | false | | | +| `ip` | [netip.Addr](#netipaddr) | false | | | +| `is_deleted` | boolean | false | | | +| `organization_id` | string | false | | | +| `request_id` | string | false | | | +| `resource_icon` | string | false | | | +| `resource_id` | string | false | | | +| `resource_link` | string | false | | | +| `resource_target` | string | false | | Resource target is the name of the resource. | +| `resource_type` | string | false | | | +| `status_code` | integer | false | | | +| `time` | string | false | | | +| `user` | [codersdk.User](#codersdkuser) | false | | | +| `user_agent` | string | false | | | + +## codersdk.AuditLogResponse + +```json +{ + "audit_logs": [ + { + "action": "string", + "additional_fields": [0], + "description": "string", + "diff": { + "property1": { + "new": null, + "old": null, + "secret": true + }, + "property2": { + "new": null, + "old": null, + "secret": true + } + }, + "id": "string", + "ip": {}, + "is_deleted": true, + "organization_id": "string", + "request_id": "string", + "resource_icon": "string", + "resource_id": "string", + "resource_link": "string", + "resource_target": "string", + "resource_type": "string", + "status_code": 0, + "time": "string", + "user": { + "avatar_url": "string", + "created_at": "string", + "email": "string", + "id": "string", + "last_seen_at": "string", + "organization_ids": ["string"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "string", + "username": "string" + }, + "user_agent": "string" + } + ], + "count": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------ | ----------------------------------------------- | -------- | ------------ | ----------- | +| `audit_logs` | array of [codersdk.AuditLog](#codersdkauditlog) | false | | | +| `count` | integer | false | | | + ## codersdk.AuthorizationCheck ```json @@ -63,35 +241,1650 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ### Properties -| Name | Type | Required | Restrictions | Description | -| ----------------- | ------ | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `organization_id` | string | false | | Organization ID (optional) adds the set constraint to all resources owned by a given organization. | -| `owner_id` | string | false | | Owner ID (optional) adds the set constraint to all resources owned by a given user. | -| `resource_id` | string | false | | Resource ID (optional) reduces the set to a singular resource. This assigns a resource ID to the resource type, eg: a single workspace. The rbac library will not fetch the resource from the database, so if you are using this option, you should also set the owner ID and organization ID if possible. Be as specific as possible using all the fields relevant. | -| `resource_type` | string | false | | Resource type is the name of the resource. `./coderd/rbac/object.go` has the list of valid resource types. | +| Name | Type | Required | Restrictions | Description | +| ----------------- | ------ | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `organization_id` | string | false | | Organization ID (optional) adds the set constraint to all resources owned by a given organization. | +| `owner_id` | string | false | | Owner ID (optional) adds the set constraint to all resources owned by a given user. | +| `resource_id` | string | false | | Resource ID (optional) reduces the set to a singular resource. This assigns a resource ID to the resource type, eg: a single workspace. The rbac library will not fetch the resource from the database, so if you are using this option, you should also set the owner ID and organization ID if possible. Be as specific as possible using all the fields relevant. | +| `resource_type` | string | false | | Resource type is the name of the resource. `./coderd/rbac/object.go` has the list of valid resource types. | + +## codersdk.AuthorizationRequest + +```json +{ + "checks": { + "property1": { + "action": "create", + "object": { + "organization_id": "string", + "owner_id": "string", + "resource_id": "string", + "resource_type": "string" + } + }, + "property2": { + "action": "create", + "object": { + "organization_id": "string", + "owner_id": "string", + "resource_id": "string", + "resource_type": "string" + } + } + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | ---------------------------------------------------------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `checks` | object | false | | Checks is a map keyed with an arbitrary string to a permission check. The key can be any string that is helpful to the caller, and allows multiple permission checks to be run in a single request. The key ensures that each permission check has the same key in the response. | +| » `[any property]` | [codersdk.AuthorizationCheck](#codersdkauthorizationcheck) | false | | It is used to check if the currently authenticated user (or the specified user) can do a given action to a given set of objects. | + +## codersdk.AuthorizationResponse + +```json +{ + "property1": true, + "property2": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------------- | ------- | -------- | ------------ | ----------- | +| `[any property]` | boolean | false | | | + +## codersdk.BuildInfoResponse + +```json +{ + "external_url": "string", + "version": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------------- | ------ | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `external_url` | string | false | | External URL references the current Coder version. For production builds, this will link directly to a release. For development builds, this will link to a commit. | +| `version` | string | false | | Version returns the semantic version of the build. | + +## codersdk.CreateParameterRequest + +```json +{ + "copy_from_parameter": "string", + "destination_scheme": "none", + "name": "string", + "source_scheme": "none", + "source_value": "string" +} +``` + +CreateParameterRequest is a structure used to create a new parameter value for a scope. + +### Properties + +| Name | Type | Required | Restrictions | Description | +| --------------------- | ------ | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `copy_from_parameter` | string | false | | Copy from parameter allows copying the value of another parameter. The other param must be related to the same template_id for this to succeed. No other fields are required if using this, as all fields will be copied from the other parameter. | +| `destination_scheme` | string | true | | | +| `name` | string | true | | | +| `source_scheme` | string | true | | | +| `source_value` | string | true | | | + +#### Enumerated Values + +| Property | Value | +| -------------------- | ---------------------- | +| `destination_scheme` | `none` | +| `destination_scheme` | `environment_variable` | +| `destination_scheme` | `provisioner_variable` | +| `source_scheme` | `none` | +| `source_scheme` | `data` | + +## codersdk.CreateTemplateRequest + +```json +{ + "allow_user_cancel_workspace_jobs": true, + "default_ttl_ms": 0, + "description": "string", + "display_name": "string", + "icon": "string", + "name": "string", + "parameter_values": [ + { + "copy_from_parameter": "string", + "destination_scheme": "none", + "name": "string", + "source_scheme": "none", + "source_value": "string" + } + ], + "template_version_id": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------- | +| `allow_user_cancel_workspace_jobs` | boolean | false | | Allow users to cancel in-progress workspace jobs. \*bool as the default value is "true". | +| `default_ttl_ms` | integer | false | | Default ttl ms allows optionally specifying the default TTL for all workspaces created from this template. | +| `description` | string | false | | Description is a description of what the template contains. It must be less than 128 bytes. | +| `display_name` | string | false | | Display name is the displayed name of the template. | +| `icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `name` | string | true | | Name is the name of the template. | +| `parameter_values` | array of [codersdk.CreateParameterRequest](#codersdkcreateparameterrequest) | false | | Parameter values is a structure used to create a new parameter value for a scope.] | +| `template_version_id` | string | true | | Template version ID is an in-progress or completed job to use as an initial version of the template. | +| This is required on creation to enable a user-flow of validating a template works. There is no reason the data-model cannot support empty templates, but it doesn't make sense for users. | + +## codersdk.CreateTestAuditLogRequest + +```json +{ + "action": "create", + "resource_id": "string", + "resource_type": "organization", + "time": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| --------------- | ------ | -------- | ------------ | ----------- | +| `action` | string | false | | | +| `resource_id` | string | false | | | +| `resource_type` | string | false | | | +| `time` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| --------------- | ------------------ | +| `action` | `create` | +| `action` | `write` | +| `action` | `delete` | +| `action` | `start` | +| `action` | `stop` | +| `resource_type` | `organization` | +| `resource_type` | `template` | +| `resource_type` | `template_version` | +| `resource_type` | `user` | +| `resource_type` | `workspace` | +| `resource_type` | `workspace_build` | +| `resource_type` | `git_ssh_key` | +| `resource_type` | `api_key` | +| `resource_type` | `group` | + +## codersdk.DERP + +```json +{ + "config": { + "path": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + }, + "server": { + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "region_code": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "region_id": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "region_name": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "relay_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "stun_addresses": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------- | ------------------------------------------------------ | -------- | ------------ | ----------- | +| `config` | [codersdk.DERPConfig](#codersdkderpconfig) | false | | | +| `server` | [codersdk.DERPServerConfig](#codersdkderpserverconfig) | false | | | + +## codersdk.DERPConfig + +```json +{ + "path": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------ | ------------------------------------------------------------------------------ | -------- | ------------ | ----------- | +| `path` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `url` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | + +## codersdk.DERPRegion + +```json +{ + "latency_ms": 0, + "preferred": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------ | ------- | -------- | ------------ | ----------- | +| `latency_ms` | number | false | | | +| `preferred` | boolean | false | | | + +## codersdk.DERPServerConfig + +```json +{ + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "region_code": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "region_id": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "region_name": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "relay_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "stun_addresses": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------------- | ------------------------------------------------------------------------------------------ | -------- | ------------ | ----------- | +| `enable` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `region_code` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `region_id` | [codersdk.DeploymentConfigField-int](#codersdkdeploymentconfigfield-int) | false | | | +| `region_name` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `relay_url` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `stun_addresses` | [codersdk.DeploymentConfigField-array_string](#codersdkdeploymentconfigfield-array_string) | false | | | + +## codersdk.DeploymentConfig + +```json +{ + "access_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "agent_fallback_troubleshooting_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "agent_stat_refresh_interval": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "api_rate_limit": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "audit_logging": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "auto_import_templates": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "autobuild_poll_interval": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "browser_only": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "cache_directory": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "derp": { + "config": { + "path": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + }, + "server": { + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "region_code": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "region_id": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "region_name": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "relay_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "stun_addresses": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + } + }, + "experimental": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "gitauth": { + "default": [ + { + "auth_url": "string", + "client_id": "string", + "id": "string", + "no_refresh": true, + "regex": "string", + "scopes": ["string"], + "token_url": "string", + "type": "string", + "validate_url": "string" + } + ], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": { + "auth_url": "string", + "client_id": "string", + "id": "string", + "no_refresh": true, + "regex": "string", + "scopes": ["string"], + "token_url": "string", + "type": "string", + "validate_url": "string" + } + }, + "http_address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "in_memory_database": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "max_token_lifetime": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "metrics_cache_refresh_interval": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "oauth2": { + "github": { + "allow_everyone": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "allow_signups": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "allowed_orgs": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "allowed_teams": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_id": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_secret": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enterprise_base_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + } + }, + "oidc": { + "allow_signups": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "client_id": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_secret": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "email_domain": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "ignore_email_verified": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "issuer_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "scopes": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + }, + "pg_connection_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "pprof": { + "address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + } + }, + "prometheus": { + "address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + } + }, + "provisioner": { + "daemon_poll_interval": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "daemon_poll_jitter": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "daemons": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "force_cancel_interval": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + } + }, + "proxy_trusted_headers": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "proxy_trusted_origins": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "scim_api_key": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "secure_auth_cookie": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "ssh_keygen_algorithm": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "swagger": { + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + } + }, + "telemetry": { + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "trace": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + }, + "tls": { + "address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "cert_file": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_auth": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_ca_file": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_cert_file": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_key_file": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "key_file": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "min_version": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "redirect_http": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + } + }, + "trace": { + "capture_logs": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "honeycomb_api_key": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } + }, + "update_check": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "wildcard_access_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | -------- | ------------ | ----------- | +| `access_url` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `address` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `agent_fallback_troubleshooting_url` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `agent_stat_refresh_interval` | [codersdk.DeploymentConfigField-time_Duration](#codersdkdeploymentconfigfield-time_duration) | false | | | +| `api_rate_limit` | [codersdk.DeploymentConfigField-int](#codersdkdeploymentconfigfield-int) | false | | | +| `audit_logging` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `auto_import_templates` | [codersdk.DeploymentConfigField-array_string](#codersdkdeploymentconfigfield-array_string) | false | | | +| `autobuild_poll_interval` | [codersdk.DeploymentConfigField-time_Duration](#codersdkdeploymentconfigfield-time_duration) | false | | | +| `browser_only` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `cache_directory` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `derp` | [codersdk.DERP](#codersdkderp) | false | | | +| `experimental` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `gitauth` | [codersdk.DeploymentConfigField-array_codersdk_GitAuthConfig](#codersdkdeploymentconfigfield-array_codersdk_gitauthconfig) | false | | | +| `http_address` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `in_memory_database` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `max_token_lifetime` | [codersdk.DeploymentConfigField-time_Duration](#codersdkdeploymentconfigfield-time_duration) | false | | | +| `metrics_cache_refresh_interval` | [codersdk.DeploymentConfigField-time_Duration](#codersdkdeploymentconfigfield-time_duration) | false | | | +| `oauth2` | [codersdk.OAuth2Config](#codersdkoauth2config) | false | | | +| `oidc` | [codersdk.OIDCConfig](#codersdkoidcconfig) | false | | | +| `pg_connection_url` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `pprof` | [codersdk.PprofConfig](#codersdkpprofconfig) | false | | | +| `prometheus` | [codersdk.PrometheusConfig](#codersdkprometheusconfig) | false | | | +| `provisioner` | [codersdk.ProvisionerConfig](#codersdkprovisionerconfig) | false | | | +| `proxy_trusted_headers` | [codersdk.DeploymentConfigField-array_string](#codersdkdeploymentconfigfield-array_string) | false | | | +| `proxy_trusted_origins` | [codersdk.DeploymentConfigField-array_string](#codersdkdeploymentconfigfield-array_string) | false | | | +| `scim_api_key` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `secure_auth_cookie` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `ssh_keygen_algorithm` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `swagger` | [codersdk.SwaggerConfig](#codersdkswaggerconfig) | false | | | +| `telemetry` | [codersdk.TelemetryConfig](#codersdktelemetryconfig) | false | | | +| `tls` | [codersdk.TLSConfig](#codersdktlsconfig) | false | | | +| `trace` | [codersdk.TraceConfig](#codersdktraceconfig) | false | | | +| `update_check` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `wildcard_access_url` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | + +## codersdk.DeploymentConfigField-array_codersdk_GitAuthConfig + +```json +{ + "default": [ + { + "auth_url": "string", + "client_id": "string", + "id": "string", + "no_refresh": true, + "regex": "string", + "scopes": ["string"], + "token_url": "string", + "type": "string", + "validate_url": "string" + } + ], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": { + "auth_url": "string", + "client_id": "string", + "id": "string", + "no_refresh": true, + "regex": "string", + "scopes": ["string"], + "token_url": "string", + "type": "string", + "validate_url": "string" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------ | --------------------------------------------------------- | -------- | ------------ | ----------- | +| `default` | array of [codersdk.GitAuthConfig](#codersdkgitauthconfig) | false | | | +| `enterprise` | boolean | false | | | +| `flag` | string | false | | | +| `hidden` | boolean | false | | | +| `name` | string | false | | | +| `secret` | boolean | false | | | +| `shorthand` | string | false | | | +| `usage` | string | false | | | +| `value` | [codersdk.GitAuthConfig](#codersdkgitauthconfig) | false | | | + +## codersdk.DeploymentConfigField-array_string + +```json +{ + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------ | --------------- | -------- | ------------ | ----------- | +| `default` | array of string | false | | | +| `enterprise` | boolean | false | | | +| `flag` | string | false | | | +| `hidden` | boolean | false | | | +| `name` | string | false | | | +| `secret` | boolean | false | | | +| `shorthand` | string | false | | | +| `usage` | string | false | | | +| `value` | string | false | | | + +## codersdk.DeploymentConfigField-bool + +```json +{ + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------ | ------- | -------- | ------------ | ----------- | +| `default` | boolean | false | | | +| `enterprise` | boolean | false | | | +| `flag` | string | false | | | +| `hidden` | boolean | false | | | +| `name` | string | false | | | +| `secret` | boolean | false | | | +| `shorthand` | string | false | | | +| `usage` | string | false | | | +| `value` | boolean | false | | | + +## codersdk.DeploymentConfigField-int + +```json +{ + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------ | ------- | -------- | ------------ | ----------- | +| `default` | integer | false | | | +| `enterprise` | boolean | false | | | +| `flag` | string | false | | | +| `hidden` | boolean | false | | | +| `name` | string | false | | | +| `secret` | boolean | false | | | +| `shorthand` | string | false | | | +| `usage` | string | false | | | +| `value` | integer | false | | | + +## codersdk.DeploymentConfigField-string + +```json +{ + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------ | ------- | -------- | ------------ | ----------- | +| `default` | string | false | | | +| `enterprise` | boolean | false | | | +| `flag` | string | false | | | +| `hidden` | boolean | false | | | +| `name` | string | false | | | +| `secret` | boolean | false | | | +| `shorthand` | string | false | | | +| `usage` | string | false | | | +| `value` | string | false | | | + +## codersdk.DeploymentConfigField-time_Duration + +```json +{ + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------ | ------- | -------- | ------------ | ----------- | +| `default` | integer | false | | | +| `enterprise` | boolean | false | | | +| `flag` | string | false | | | +| `hidden` | boolean | false | | | +| `name` | string | false | | | +| `secret` | boolean | false | | | +| `shorthand` | string | false | | | +| `usage` | string | false | | | +| `value` | integer | false | | | + +## codersdk.GetAppHostResponse + +```json +{ + "host": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------ | ------ | -------- | ------------ | ------------------------------------------------------------- | +| `host` | string | false | | Host is the externally accessible URL for the Coder instance. | + +## codersdk.GitAuthConfig + +```json +{ + "auth_url": "string", + "client_id": "string", + "id": "string", + "no_refresh": true, + "regex": "string", + "scopes": ["string"], + "token_url": "string", + "type": "string", + "validate_url": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------------- | --------------- | -------- | ------------ | ----------- | +| `auth_url` | string | false | | | +| `client_id` | string | false | | | +| `id` | string | false | | | +| `no_refresh` | boolean | false | | | +| `regex` | string | false | | | +| `scopes` | array of string | false | | | +| `token_url` | string | false | | | +| `type` | string | false | | | +| `validate_url` | string | false | | | + +## codersdk.Healthcheck + +```json +{ + "interval": 0, + "threshold": 0, + "url": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------------------------ | +| `interval` | integer | false | | Interval specifies the seconds between each health check. | +| `threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `url` | string | false | | URL specifies the endpoint to check for the app health. | -## codersdk.AuthorizationRequest +## codersdk.OAuth2Config ```json { - "checks": { - "property1": { - "action": "create", - "object": { - "organization_id": "string", - "owner_id": "string", - "resource_id": "string", - "resource_type": "string" - } + "github": { + "allow_everyone": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true }, - "property2": { - "action": "create", - "object": { - "organization_id": "string", - "owner_id": "string", - "resource_id": "string", - "resource_type": "string" - } + "allow_signups": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "allowed_orgs": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "allowed_teams": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_id": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_secret": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enterprise_base_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" } } } @@ -99,158 +1892,376 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ### Properties -| Name | Type | Required | Restrictions | Description | -| ------------------ | ---------------------------------------------------------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `checks` | object | false | | Checks is a map keyed with an arbitrary string to a permission check. The key can be any string that is helpful to the caller, and allows multiple permission checks to be run in a single request. The key ensures that each permission check has the same key in the response. | -| » `[any property]` | [codersdk.AuthorizationCheck](#codersdkauthorizationcheck) | false | | It is used to check if the currently authenticated user (or the specified user) can do a given action to a given set of objects. | +| Name | Type | Required | Restrictions | Description | +| -------- | ---------------------------------------------------------- | -------- | ------------ | ----------- | +| `github` | [codersdk.OAuth2GithubConfig](#codersdkoauth2githubconfig) | false | | | -## codersdk.AuthorizationResponse +## codersdk.OAuth2GithubConfig ```json { - "property1": true, - "property2": true + "allow_everyone": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "allow_signups": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "allowed_orgs": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "allowed_teams": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_id": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_secret": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enterprise_base_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ---------------- | ------- | -------- | ------------ | ----------- | -| `[any property]` | boolean | false | | | +| Name | Type | Required | Restrictions | Description | +| --------------------- | ------------------------------------------------------------------------------------------ | -------- | ------------ | ----------- | +| `allow_everyone` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `allow_signups` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `allowed_orgs` | [codersdk.DeploymentConfigField-array_string](#codersdkdeploymentconfigfield-array_string) | false | | | +| `allowed_teams` | [codersdk.DeploymentConfigField-array_string](#codersdkdeploymentconfigfield-array_string) | false | | | +| `client_id` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `client_secret` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `enterprise_base_url` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | -## codersdk.BuildInfoResponse +## codersdk.OIDCConfig ```json { - "external_url": "string", - "version": "string" + "allow_signups": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "client_id": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_secret": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "email_domain": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "ignore_email_verified": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "issuer_url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "scopes": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| -------------- | ------ | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `external_url` | string | false | | External URL references the current Coder version. For production builds, this will link directly to a release. For development builds, this will link to a commit. | -| `version` | string | false | | Version returns the semantic version of the build. | +| Name | Type | Required | Restrictions | Description | +| ----------------------- | ------------------------------------------------------------------------------------------ | -------- | ------------ | ----------- | +| `allow_signups` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `client_id` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `client_secret` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `email_domain` | [codersdk.DeploymentConfigField-array_string](#codersdkdeploymentconfigfield-array_string) | false | | | +| `ignore_email_verified` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `issuer_url` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `scopes` | [codersdk.DeploymentConfigField-array_string](#codersdkdeploymentconfigfield-array_string) | false | | | -## codersdk.CreateParameterRequest +## codersdk.Parameter ```json { - "copy_from_parameter": "string", - "destination_scheme": "environment_variable", + "created_at": "string", + "destination_scheme": "none", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "name": "string", - "source_scheme": "data", - "source_value": "string" + "scope": "template", + "scope_id": "5d3fe357-12dd-4f62-b004-6d1fb3b8454f", + "source_scheme": "none", + "updated_at": "string" } ``` -CreateParameterRequest is a structure used to create a new parameter value for a scope. +Parameter represents a set value for the scope. ### Properties -| Name | Type | Required | Restrictions | Description | -| --------------------- | ------ | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `copy_from_parameter` | string | false | | Copy from parameter allows copying the value of another parameter. The other param must be related to the same template_id for this to succeed. No other fields are required if using this, as all fields will be copied from the other parameter. | -| `destination_scheme` | string | true | | | -| `name` | string | true | | | -| `source_scheme` | string | true | | | -| `source_value` | string | true | | | +| Name | Type | Required | Restrictions | Description | +| -------------------- | ------ | -------- | ------------ | ----------- | +| `created_at` | string | false | | | +| `destination_scheme` | string | false | | | +| `id` | string | false | | | +| `name` | string | false | | | +| `scope` | string | false | | | +| `scope_id` | string | false | | | +| `source_scheme` | string | false | | | +| `updated_at` | string | false | | | #### Enumerated Values | Property | Value | | -------------------- | ---------------------- | +| `destination_scheme` | `none` | | `destination_scheme` | `environment_variable` | | `destination_scheme` | `provisioner_variable` | +| `scope` | `template` | +| `scope` | `workspace` | +| `scope` | `import_job` | +| `source_scheme` | `none` | | `source_scheme` | `data` | -## codersdk.CreateTemplateRequest - -```json -{ - "allow_user_cancel_workspace_jobs": true, - "default_ttl_ms": 0, - "description": "string", - "display_name": "string", - "icon": "string", - "name": "string", - "parameter_values": [ - { - "copy_from_parameter": "string", - "destination_scheme": "environment_variable", - "name": "string", - "source_scheme": "data", - "source_value": "string" - } - ], - "template_version_id": "string" -} -``` - -### Properties - -| Name | Type | Required | Restrictions | Description | -| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------- | -| `allow_user_cancel_workspace_jobs` | boolean | false | | Allow users to cancel in-progress workspace jobs. \*bool as the default value is "true". | -| `default_ttl_ms` | integer | false | | Default ttl ms allows optionally specifying the default TTL for all workspaces created from this template. | -| `description` | string | false | | Description is a description of what the template contains. It must be less than 128 bytes. | -| `display_name` | string | false | | Display name is the displayed name of the template. | -| `icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | -| `name` | string | true | | Name is the name of the template. | -| `parameter_values` | array of [codersdk.CreateParameterRequest](#codersdkcreateparameterrequest) | false | | Parameter values is a structure used to create a new parameter value for a scope.] | -| `template_version_id` | string | true | | Template version ID is an in-progress or completed job to use as an initial version of the template. | -| This is required on creation to enable a user-flow of validating a template works. There is no reason the data-model cannot support empty templates, but it doesn't make sense for users. | - -## codersdk.DERPRegion +## codersdk.PprofConfig ```json { - "latency_ms": 0, - "preferred": true + "address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + } } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ------------ | ------- | -------- | ------------ | ----------- | -| `latency_ms` | number | false | | | -| `preferred` | boolean | false | | | +| Name | Type | Required | Restrictions | Description | +| --------- | ------------------------------------------------------------------------------ | -------- | ------------ | ----------- | +| `address` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `enable` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | -## codersdk.GetAppHostResponse +## codersdk.PrometheusConfig ```json { - "host": "string" + "address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + } } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ------ | ------ | -------- | ------------ | ------------------------------------------------------------- | -| `host` | string | false | | Host is the externally accessible URL for the Coder instance. | +| Name | Type | Required | Restrictions | Description | +| --------- | ------------------------------------------------------------------------------ | -------- | ------------ | ----------- | +| `address` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `enable` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | -## codersdk.Healthcheck +## codersdk.ProvisionerConfig ```json { - "interval": 0, - "threshold": 0, - "url": "string" + "daemon_poll_interval": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "daemon_poll_jitter": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "daemons": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + }, + "force_cancel_interval": { + "default": 0, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": 0 + } } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ----------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------------------------ | -| `interval` | integer | false | | Interval specifies the seconds between each health check. | -| `threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `url` | string | false | | URL specifies the endpoint to check for the app health. | +| Name | Type | Required | Restrictions | Description | +| ----------------------- | -------------------------------------------------------------------------------------------- | -------- | ------------ | ----------- | +| `daemon_poll_interval` | [codersdk.DeploymentConfigField-time_Duration](#codersdkdeploymentconfigfield-time_duration) | false | | | +| `daemon_poll_jitter` | [codersdk.DeploymentConfigField-time_Duration](#codersdkdeploymentconfigfield-time_duration) | false | | | +| `daemons` | [codersdk.DeploymentConfigField-int](#codersdkdeploymentconfigfield-int) | false | | | +| `force_cancel_interval` | [codersdk.DeploymentConfigField-time_Duration](#codersdkdeploymentconfigfield-time_duration) | false | | | ## codersdk.ProvisionerJob @@ -325,6 +2336,226 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `message` | string | false | | Message is an actionable message that depicts actions the request took. These messages should be fully formed sentences with proper punctuation. Examples: - "A user has been created." - "Failed to create a user." | | `validations` | array of [codersdk.ValidationError](#codersdkvalidationerror) | false | | Validations are form field-specific friendly error messages. They will be shown on a form field in the UI. These can also be used to add additional context if there is a set of errors in the primary 'Message'. | +## codersdk.Role + +```json +{ + "display_name": "string", + "name": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------------- | ------ | -------- | ------------ | ----------- | +| `display_name` | string | false | | | +| `name` | string | false | | | + +## codersdk.SwaggerConfig + +```json +{ + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------- | -------------------------------------------------------------------------- | -------- | ------------ | ----------- | +| `enable` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | + +## codersdk.TLSConfig + +```json +{ + "address": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "cert_file": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_auth": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_ca_file": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_cert_file": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "client_key_file": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "key_file": { + "default": ["string"], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "min_version": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + }, + "redirect_http": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | ------------------------------------------------------------------------------------------ | -------- | ------------ | ----------- | +| `address` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `cert_file` | [codersdk.DeploymentConfigField-array_string](#codersdkdeploymentconfigfield-array_string) | false | | | +| `client_auth` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `client_ca_file` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `client_cert_file` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `client_key_file` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `enable` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `key_file` | [codersdk.DeploymentConfigField-array_string](#codersdkdeploymentconfigfield-array_string) | false | | | +| `min_version` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | +| `redirect_http` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | + +## codersdk.TelemetryConfig + +```json +{ + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "trace": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "url": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------- | ------------------------------------------------------------------------------ | -------- | ------------ | ----------- | +| `enable` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `trace` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `url` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | + ## codersdk.Template ```json @@ -401,6 +2632,54 @@ CreateParameterRequest is a structure used to create a new parameter value for a | ---------------- | ---------------------------------------------------- | -------- | ------------ | ----------- | | `[any property]` | [codersdk.TransitionStats](#codersdktransitionstats) | false | | | +## codersdk.TraceConfig + +```json +{ + "capture_logs": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "enable": { + "default": true, + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": true + }, + "honeycomb_api_key": { + "default": "string", + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": "string" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------- | ------------------------------------------------------------------------------ | -------- | ------------ | ----------- | +| `capture_logs` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `enable` | [codersdk.DeploymentConfigField-bool](#codersdkdeploymentconfigfield-bool) | false | | | +| `honeycomb_api_key` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | + ## codersdk.TransitionStats ```json @@ -477,6 +2756,55 @@ CreateParameterRequest is a structure used to create a new parameter value for a | -------- | ------- | -------- | ------------ | ----------- | | `ttl_ms` | integer | false | | | +## codersdk.UploadResponse + +```json +{ + "hash": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------ | ------ | -------- | ------------ | ----------- | +| `hash` | string | false | | | + +## codersdk.User + +```json +{ + "avatar_url": "string", + "created_at": "string", + "email": "string", + "id": "string", + "last_seen_at": "string", + "organization_ids": ["string"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "string", + "username": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | --------------------------------------- | -------- | ------------ | ----------- | +| `avatar_url` | string | false | | | +| `created_at` | string | true | | | +| `email` | string | true | | | +| `id` | string | true | | | +| `last_seen_at` | string | false | | | +| `organization_ids` | array of string | false | | | +| `roles` | array of [codersdk.Role](#codersdkrole) | false | | | +| `status` | string | false | | | +| `username` | string | true | | | + ## codersdk.ValidationError ```json @@ -505,10 +2833,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a "build_number": 0, "created_at": "2019-08-24T14:15:22Z", "daily_cost": 0, - "deadline": { - "time": "string", - "valid": true - }, + "deadline": "2019-08-24T14:15:22Z", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", @@ -776,10 +3101,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a "build_number": 0, "created_at": "2019-08-24T14:15:22Z", "daily_cost": 0, - "deadline": { - "time": "string", - "valid": true - }, + "deadline": "2019-08-24T14:15:22Z", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", @@ -892,7 +3214,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `build_number` | integer | false | | | | `created_at` | string | false | | | | `daily_cost` | integer | false | | | -| `deadline` | string(time) or `null` | false | | | +| `deadline` | string | false | | | | `id` | string | false | | | | `initiator_id` | string | false | | | | `initiator_name` | string | false | | | @@ -1061,10 +3383,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a "build_number": 0, "created_at": "2019-08-24T14:15:22Z", "daily_cost": 0, - "deadline": { - "time": "string", - "valid": true - }, + "deadline": "2019-08-24T14:15:22Z", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", @@ -1186,3 +3505,13 @@ CreateParameterRequest is a structure used to create a new parameter value for a | ------------ | ------------------------------------------------- | -------- | ------------ | ----------- | | `count` | integer | false | | | | `workspaces` | array of [codersdk.Workspace](#codersdkworkspace) | false | | | + +## netip.Addr + +```json +{} +``` + +### Properties + +_None_ diff --git a/docs/api/templates.md b/docs/api/templates.md index 23a7260494db6..0cde6eaef5926 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -29,9 +29,9 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization-id}/tem "parameter_values": [ { "copy_from_parameter": "string", - "destination_scheme": "environment_variable", + "destination_scheme": "none", "name": "string", - "source_scheme": "data", + "source_scheme": "none", "source_value": "string" } ], @@ -244,7 +244,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. -## Update template metadata by ID +## Get template metadata by ID ### Code samples @@ -349,3 +349,65 @@ curl -X DELETE http://coder-server:8080/api/v2/templates/{id} \ | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Update template metadata by ID + +### Code samples + +```shell +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/templates/{id} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /templates/{id}` + +### Parameters + +| Name | In | Type | Required | Description | +| ---- | ---- | ------------ | -------- | ----------- | +| `id` | path | string(uuid) | true | Template ID | + +### Example responses + +> 200 Response + +```json +{ + "active_user_count": 0, + "active_version_id": "string", + "allow_user_cancel_workspace_jobs": true, + "build_time_stats": { + "property1": { + "p50": 123, + "p95": 146 + }, + "property2": { + "p50": 123, + "p95": 146 + } + }, + "created_at": "2019-08-24T14:15:22Z", + "created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f", + "created_by_name": "string", + "default_ttl_ms": 0, + "description": "string", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "provisioner": "string", + "updated_at": "2019-08-24T14:15:22Z", + "workspace_owner_count": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Template](schemas.md#codersdktemplate) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index fcbfcda60d60e..4739d86a90715 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -36,10 +36,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member "build_number": 0, "created_at": "2019-08-24T14:15:22Z", "daily_cost": 0, - "deadline": { - "time": "string", - "valid": true - }, + "deadline": "2019-08-24T14:15:22Z", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", @@ -200,10 +197,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "build_number": 0, "created_at": "2019-08-24T14:15:22Z", "daily_cost": 0, - "deadline": { - "time": "string", - "valid": true - }, + "deadline": "2019-08-24T14:15:22Z", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", @@ -387,10 +381,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "build_number": 0, "created_at": "2019-08-24T14:15:22Z", "daily_cost": 0, - "deadline": { - "time": "string", - "valid": true - }, + "deadline": "2019-08-24T14:15:22Z", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", @@ -548,10 +539,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{id} \ "build_number": 0, "created_at": "2019-08-24T14:15:22Z", "daily_cost": 0, - "deadline": { - "time": "string", - "valid": true - }, + "deadline": "2019-08-24T14:15:22Z", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", diff --git a/docs/manifest.json b/docs/manifest.json index 5e3e78386ea03..5e20465ae272e 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -337,10 +337,18 @@ "title": "Applications", "path": "./api/applications.md" }, + { + "title": "Audit", + "path": "./api/audit.md" + }, { "title": "Authorization", "path": "./api/authorization.md" }, + { + "title": "Files", + "path": "./api/files.md" + }, { "title": "Templates", "path": "./api/templates.md" @@ -349,6 +357,10 @@ "title": "Workspaces", "path": "./api/workspaces.md" }, + { + "title": "Parameters", + "path": "./api/parameters.md" + }, { "title": "Schemas", "path": "./api/schemas.md" diff --git a/scripts/apidocgen/markdown-template/code_shell.dot b/scripts/apidocgen/markdown-template/code_shell.dot index 041b3d92a0344..7300ae4ab29aa 100644 --- a/scripts/apidocgen/markdown-template/code_shell.dot +++ b/scripts/apidocgen/markdown-template/code_shell.dot @@ -1,4 +1,8 @@ # Example request using curl curl -X {{=data.methodUpper}} http://coder-server:8080{{=data.url}}{{=data.requiredQueryString}}{{?data.allHeaders.length}} \{{?}} -{{~data.allHeaders :p:index}} -H '{{=p.name}}: {{=p.exampleValues.object}}'{{?index < data.allHeaders.length-1}} \ +{{~data.allHeaders :p:index}}{{ + if (p.name == "Content-Type" && p.exampleValues.object == "application/x-www-form-urlencoded") { + continue; + } +}} -H '{{=p.name}}: {{=p.exampleValues.object}}'{{?index < data.allHeaders.length-1}} \ {{?}}{{~}} diff --git a/scripts/apidocgen/markdown-template/main.dot b/scripts/apidocgen/markdown-template/main.dot index d040a95fa4490..59236d94a9fbd 100644 --- a/scripts/apidocgen/markdown-template/main.dot +++ b/scripts/apidocgen/markdown-template/main.dot @@ -5,9 +5,6 @@ } const pRef = p.$ref.replace("#/components/schemas/",""); - if (pRef == "codersdk.NullTime") { - return "string(time) or `null`"; - } return "[" + pRef + "](#" + pRef.replace(".","").toLowerCase() + ")"; } diff --git a/scripts/apidocgen/markdown-template/operation.dot b/scripts/apidocgen/markdown-template/operation.dot index f0363bcbabf45..e5d833172facd 100644 --- a/scripts/apidocgen/markdown-template/operation.dot +++ b/scripts/apidocgen/markdown-template/operation.dot @@ -48,7 +48,5 @@ {{ data.security = data.operation.security ? data.operation.security : data.api.security; }} {{? data.security && data.security.length }} {{#def.authentication}} -{{??}} -{{#def.authentication_none}} {{?}} {{= data.tags.endSection }} From fbe1d7088eb7fe90a56b0318448d934859294be7 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 2 Jan 2023 12:12:53 +0100 Subject: [PATCH 02/61] Swagger comments in workspacebuilds.go --- coderd/apidoc/docs.go | 217 ++++++++++++++ coderd/apidoc/swagger.json | 197 +++++++++++++ coderd/workspacebuilds.go | 43 +++ docs/api/schemas.md | 24 ++ docs/api/workspaces.md | 565 +++++++++++++++++++++++++++++++++++++ 5 files changed, 1046 insertions(+) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 5ff12f9fb8f8f..8249ee27fa45d 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -875,6 +875,200 @@ const docTemplate = `{ } } }, + "/workspacebuilds/{workspacebuild}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Workspaces" + ], + "summary": "Get workspace build", + "operationId": "get-workspace-build", + "parameters": [ + { + "type": "string", + "description": "Workspace build ID", + "name": "workspacebuild", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceBuild" + } + } + } + } + }, + "/workspacebuilds/{workspacebuild}/cancel": { + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Workspaces" + ], + "summary": "Cancel workspace build", + "operationId": "cancel-workspace-build", + "parameters": [ + { + "type": "string", + "description": "Workspace build ID", + "name": "workspacebuild", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, + "/workspacebuilds/{workspacebuild}/logs": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Workspaces" + ], + "summary": "Get workspace build logs", + "operationId": "get-workspace-build-logs", + "parameters": [ + { + "type": "string", + "description": "Workspace build ID", + "name": "workspacebuild", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Before Unix timestamp", + "name": "before", + "in": "query" + }, + { + "type": "integer", + "description": "After Unix timestamp", + "name": "after", + "in": "query" + }, + { + "type": "boolean", + "description": "Follow log stream", + "name": "follow", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ProvisionerJobLog" + } + } + } + } + } + }, + "/workspacebuilds/{workspacebuild}/resources": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Workspaces" + ], + "summary": "Get workspace resources for workspace build", + "operationId": "get-workspace-resources-for-workspace-build", + "parameters": [ + { + "type": "string", + "description": "Workspace build ID", + "name": "workspacebuild", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceResource" + } + } + } + } + } + }, + "/workspacebuilds/{workspacebuild}/state": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Workspaces" + ], + "summary": "Get provisioner state for workspace build", + "operationId": "get-provisioner-state-for-workspace-build", + "parameters": [ + { + "type": "string", + "description": "Workspace build ID", + "name": "workspacebuild", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceBuild" + } + } + } + } + }, "/workspaces": { "get": { "security": [ @@ -2101,6 +2295,29 @@ const docTemplate = `{ } } }, + "codersdk.ProvisionerJobLog": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "log_level": { + "type": "string" + }, + "log_source": { + "type": "string" + }, + "output": { + "type": "string" + }, + "stage": { + "type": "string" + } + } + }, "codersdk.PutExtendWorkspaceRequest": { "type": "object", "required": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index ef30670b3a406..0dc437f5118b4 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -757,6 +757,180 @@ } } }, + "/workspacebuilds/{workspacebuild}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Workspaces"], + "summary": "Get workspace build", + "operationId": "get-workspace-build", + "parameters": [ + { + "type": "string", + "description": "Workspace build ID", + "name": "workspacebuild", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceBuild" + } + } + } + } + }, + "/workspacebuilds/{workspacebuild}/cancel": { + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Workspaces"], + "summary": "Cancel workspace build", + "operationId": "cancel-workspace-build", + "parameters": [ + { + "type": "string", + "description": "Workspace build ID", + "name": "workspacebuild", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, + "/workspacebuilds/{workspacebuild}/logs": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Workspaces"], + "summary": "Get workspace build logs", + "operationId": "get-workspace-build-logs", + "parameters": [ + { + "type": "string", + "description": "Workspace build ID", + "name": "workspacebuild", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Before Unix timestamp", + "name": "before", + "in": "query" + }, + { + "type": "integer", + "description": "After Unix timestamp", + "name": "after", + "in": "query" + }, + { + "type": "boolean", + "description": "Follow log stream", + "name": "follow", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ProvisionerJobLog" + } + } + } + } + } + }, + "/workspacebuilds/{workspacebuild}/resources": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Workspaces"], + "summary": "Get workspace resources for workspace build", + "operationId": "get-workspace-resources-for-workspace-build", + "parameters": [ + { + "type": "string", + "description": "Workspace build ID", + "name": "workspacebuild", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceResource" + } + } + } + } + } + }, + "/workspacebuilds/{workspacebuild}/state": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Workspaces"], + "summary": "Get provisioner state for workspace build", + "operationId": "get-provisioner-state-for-workspace-build", + "parameters": [ + { + "type": "string", + "description": "Workspace build ID", + "name": "workspacebuild", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceBuild" + } + } + } + } + }, "/workspaces": { "get": { "security": [ @@ -1910,6 +2084,29 @@ } } }, + "codersdk.ProvisionerJobLog": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "log_level": { + "type": "string" + }, + "log_source": { + "type": "string" + }, + "output": { + "type": "string" + }, + "stage": { + "type": "string" + } + } + }, "codersdk.PutExtendWorkspaceRequest": { "type": "object", "required": ["deadline"], diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index a006c932c752b..eee08bad5ec0d 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -23,6 +23,14 @@ import ( "github.com/coder/coder/codersdk" ) +// @Summary Get workspace build +// @ID get-workspace-build +// @Security CoderSessionToken +// @Produce json +// @Tags Workspaces +// @Param workspacebuild path string true "Workspace build ID" +// @Success 200 {object} codersdk.WorkspaceBuild +// @Router /workspacebuilds/{workspacebuild} [get] func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceBuild := httpmw.WorkspaceBuildParam(r) @@ -535,6 +543,14 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusCreated, apiBuild) } +// @Summary Cancel workspace build +// @ID cancel-workspace-build +// @Security CoderSessionToken +// @Produce json +// @Tags Workspaces +// @Param workspacebuild path string true "Workspace build ID" +// @Success 200 {object} codersdk.Response +// @Router /workspacebuilds/{workspacebuild}/cancel [patch] func (api *API) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceBuild := httpmw.WorkspaceBuildParam(r) @@ -630,6 +646,14 @@ func (api *API) verifyUserCanCancelWorkspaceBuilds(ctx context.Context, userID u return slices.Contains(user.RBACRoles, rbac.RoleOwner()), nil // only user with "owner" role can cancel workspace builds } +// @Summary Get workspace resources for workspace build +// @ID get-workspace-resources-for-workspace-build +// @Security CoderSessionToken +// @Produce json +// @Tags Workspaces +// @Param workspacebuild path string true "Workspace build ID" +// @Success 200 {array} codersdk.WorkspaceResource +// @Router /workspacebuilds/{workspacebuild}/resources [get] func (api *API) workspaceBuildResources(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceBuild := httpmw.WorkspaceBuildParam(r) @@ -657,6 +681,17 @@ func (api *API) workspaceBuildResources(rw http.ResponseWriter, r *http.Request) api.provisionerJobResources(rw, r, job) } +// @Summary Get workspace build logs +// @ID get-workspace-build-logs +// @Security CoderSessionToken +// @Produce json +// @Tags Workspaces +// @Param workspacebuild path string true "Workspace build ID" +// @Param before query int false "Before Unix timestamp" +// @Param after query int false "After Unix timestamp" +// @Param follow query bool false "Follow log stream" +// @Success 200 {array} codersdk.ProvisionerJobLog +// @Router /workspacebuilds/{workspacebuild}/logs [get] func (api *API) workspaceBuildLogs(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceBuild := httpmw.WorkspaceBuildParam(r) @@ -684,6 +719,14 @@ func (api *API) workspaceBuildLogs(rw http.ResponseWriter, r *http.Request) { api.provisionerJobLogs(rw, r, job) } +// @Summary Get provisioner state for workspace build +// @ID get-provisioner-state-for-workspace-build +// @Security CoderSessionToken +// @Produce json +// @Tags Workspaces +// @Param workspacebuild path string true "Workspace build ID" +// @Success 200 {object} codersdk.WorkspaceBuild +// @Router /workspacebuilds/{workspacebuild}/state [get] func (api *API) workspaceBuildState(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceBuild := httpmw.WorkspaceBuildParam(r) diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 369d9d2688cc8..dddd6804a3ec0 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2299,6 +2299,30 @@ Parameter represents a set value for the scope. | » `[any property]` | string | false | | | | `worker_id` | string | false | | | +## codersdk.ProvisionerJobLog + +```json +{ + "created_at": "string", + "id": 0, + "log_level": "string", + "log_source": "string", + "output": "string", + "stage": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------ | ------- | -------- | ------------ | ----------- | +| `created_at` | string | false | | | +| `id` | integer | false | | | +| `log_level` | string | false | | | +| `log_source` | string | false | | | +| `output` | string | false | | | +| `stage` | string | false | | | + ## codersdk.PutExtendWorkspaceRequest ```json diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 4739d86a90715..0bd455c5ef8d1 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -323,6 +323,571 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. +## Get workspace build + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspacebuilds/{workspacebuild}` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------ | -------- | ------------------ | +| `workspacebuild` | path | string | true | Workspace build ID | + +### Example responses + +> 200 Response + +```json +{ + "build_number": 0, + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "deadline": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", + "initiator_name": "string", + "job": { + "canceled_at": "string", + "completed_at": "string", + "created_at": "string", + "error": "string", + "file_id": "string", + "id": "string", + "started_at": "string", + "status": "string", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "string" + }, + "reason": "string", + "resources": [ + { + "agents": [ + { + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "string", + "sharing_level": "string", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "architecture": "string", + "connection_timeout_seconds": 0, + "created_at": "string", + "directory": "string", + "disconnected_at": "string", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "first_connected_at": "string", + "id": "string", + "instance_id": "string", + "last_connected_at": "string", + "latency": { + "property1": { + "latency_ms": 0, + "preferred": true + }, + "property2": { + "latency_ms": 0, + "preferred": true + } + }, + "name": "string", + "operating_system": "string", + "resource_id": "string", + "startup_script": "string", + "status": "string", + "troubleshooting_url": "string", + "updated_at": "string", + "version": "string" + } + ], + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "hide": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "metadata": [ + { + "key": "string", + "sensitive": true, + "value": "string" + } + ], + "name": "string", + "type": "string", + "workspace_transition": "start" + } + ], + "status": "pending", + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", + "template_version_name": "string", + "transition": "start", + "updated_at": "2019-08-24T14:15:22Z", + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9", + "workspace_name": "string", + "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", + "workspace_owner_name": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceBuild](schemas.md#codersdkworkspacebuild) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Cancel workspace build + +### Code samples + +```shell +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/cancel \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /workspacebuilds/{workspacebuild}/cancel` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------ | -------- | ------------------ | +| `workspacebuild` | path | string | true | Workspace build ID | + +### Example responses + +> 200 Response + +```json +{ + "detail": "string", + "message": "string", + "validations": [ + { + "detail": "string", + "field": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get workspace build logs + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/logs \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspacebuilds/{workspacebuild}/logs` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ----- | ------- | -------- | --------------------- | +| `workspacebuild` | path | string | true | Workspace build ID | +| `before` | query | integer | false | Before Unix timestamp | +| `after` | query | integer | false | After Unix timestamp | +| `follow` | query | boolean | false | Follow log stream | + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "string", + "id": 0, + "log_level": "string", + "log_source": "string", + "output": "string", + "stage": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.ProvisionerJobLog](schemas.md#codersdkprovisionerjoblog) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| -------------- | ------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string | false | | | +| `» id` | integer | false | | | +| `» log_level` | string | false | | | +| `» log_source` | string | false | | | +| `» output` | string | false | | | +| `» stage` | string | false | | | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get workspace resources for workspace build + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/resources \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspacebuilds/{workspacebuild}/resources` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------ | -------- | ------------------ | +| `workspacebuild` | path | string | true | Workspace build ID | + +### Example responses + +> 200 Response + +```json +[ + { + "agents": [ + { + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "string", + "sharing_level": "string", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "architecture": "string", + "connection_timeout_seconds": 0, + "created_at": "string", + "directory": "string", + "disconnected_at": "string", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "first_connected_at": "string", + "id": "string", + "instance_id": "string", + "last_connected_at": "string", + "latency": { + "property1": { + "latency_ms": 0, + "preferred": true + }, + "property2": { + "latency_ms": 0, + "preferred": true + } + }, + "name": "string", + "operating_system": "string", + "resource_id": "string", + "startup_script": "string", + "status": "string", + "troubleshooting_url": "string", + "updated_at": "string", + "version": "string" + } + ], + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "hide": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "metadata": [ + { + "key": "string", + "sensitive": true, + "value": "string" + } + ], + "name": "string", + "type": "string", + "workspace_transition": "start" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.WorkspaceResource](schemas.md#codersdkworkspaceresource) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | ---------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | DisplayName is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on
the client or not. | +| `»»» health` | string | false | | | +| `»»» healthcheck` | `codersdk.Healthcheck` | false | | | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies
an icon to be displayed in the dashboard. | +| `»»» id` | string | false | | | +| `»»» sharing_level` | string | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the
`coder server` or via a hostname-based dev URL. If this is set to true
and there is no app wildcard configured on the server, the app will not
be accessible in the UI. | +| `»»» url` | string | false | | URL is the address being proxied to inside the workspace.
If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» first_connected_at` | string | false | | | +| `»» id` | string | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string | false | | | +| `»» latency` | object | false | | DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | `codersdk.DERPRegion` | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» resource_id` | string | false | | | +| `»» startup_script` | string | false | | | +| `»» status` | string | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string | false | | | +| `»» version` | string | false | | | +| `» created_at` | string | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string | false | | | +| `» job_id` | string | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| ---------------------- | -------- | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get provisioner state for workspace build + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/state \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspacebuilds/{workspacebuild}/state` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------ | -------- | ------------------ | +| `workspacebuild` | path | string | true | Workspace build ID | + +### Example responses + +> 200 Response + +```json +{ + "build_number": 0, + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "deadline": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", + "initiator_name": "string", + "job": { + "canceled_at": "string", + "completed_at": "string", + "created_at": "string", + "error": "string", + "file_id": "string", + "id": "string", + "started_at": "string", + "status": "string", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "string" + }, + "reason": "string", + "resources": [ + { + "agents": [ + { + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "string", + "sharing_level": "string", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "architecture": "string", + "connection_timeout_seconds": 0, + "created_at": "string", + "directory": "string", + "disconnected_at": "string", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "first_connected_at": "string", + "id": "string", + "instance_id": "string", + "last_connected_at": "string", + "latency": { + "property1": { + "latency_ms": 0, + "preferred": true + }, + "property2": { + "latency_ms": 0, + "preferred": true + } + }, + "name": "string", + "operating_system": "string", + "resource_id": "string", + "startup_script": "string", + "status": "string", + "troubleshooting_url": "string", + "updated_at": "string", + "version": "string" + } + ], + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "hide": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "metadata": [ + { + "key": "string", + "sensitive": true, + "value": "string" + } + ], + "name": "string", + "type": "string", + "workspace_transition": "start" + } + ], + "status": "pending", + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", + "template_version_name": "string", + "transition": "start", + "updated_at": "2019-08-24T14:15:22Z", + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9", + "workspace_name": "string", + "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", + "workspace_owner_name": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceBuild](schemas.md#codersdkworkspacebuild) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + ## List workspaces ### Code samples From 96daf47a50d43af4d1251f9092f53143999c8309 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 2 Jan 2023 13:47:34 +0100 Subject: [PATCH 03/61] structs in workspacebuilds.go --- coderd/apidoc/docs.go | 50 +++++++++++--- coderd/apidoc/swagger.json | 40 ++++++++--- codersdk/provisionerdaemons.go | 22 +++--- codersdk/workspacebuilds.go | 5 +- docs/api/schemas.md | 98 ++++++++++++++++---------- docs/api/workspaces.md | 122 ++++++++++++++++++--------------- 6 files changed, 211 insertions(+), 126 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 8249ee27fa45d..a4fd764ceba6a 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2261,28 +2261,42 @@ const docTemplate = `{ "type": "object", "properties": { "canceled_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "completed_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "created_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "error": { "type": "string" }, "file_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "started_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "status": { - "type": "string" + "type": "string", + "enum": [ + "pending", + "running", + "succeeded", + "canceling", + "canceled", + "failed" + ] }, "tags": { "type": "object", @@ -2291,7 +2305,8 @@ const docTemplate = `{ } }, "worker_id": { - "type": "string" + "type": "string", + "format": "uuid" } } }, @@ -2299,13 +2314,21 @@ const docTemplate = `{ "type": "object", "properties": { "created_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "id": { "type": "integer" }, "log_level": { - "type": "string" + "type": "string", + "enum": [ + "trace", + "debug", + "info", + "warn", + "error" + ] }, "log_source": { "type": "string" @@ -2831,7 +2854,12 @@ const docTemplate = `{ "$ref": "#/definitions/codersdk.ProvisionerJob" }, "reason": { - "type": "string" + "type": "string", + "enum": [ + "initiator", + "autostart", + "autostop" + ] }, "resources": { "type": "array", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 0dc437f5118b4..7e12c5beb8c1f 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -2050,28 +2050,42 @@ "type": "object", "properties": { "canceled_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "completed_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "created_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "error": { "type": "string" }, "file_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "started_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "status": { - "type": "string" + "type": "string", + "enum": [ + "pending", + "running", + "succeeded", + "canceling", + "canceled", + "failed" + ] }, "tags": { "type": "object", @@ -2080,7 +2094,8 @@ } }, "worker_id": { - "type": "string" + "type": "string", + "format": "uuid" } } }, @@ -2088,13 +2103,15 @@ "type": "object", "properties": { "created_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "id": { "type": "integer" }, "log_level": { - "type": "string" + "type": "string", + "enum": ["trace", "debug", "info", "warn", "error"] }, "log_source": { "type": "string" @@ -2610,7 +2627,8 @@ "$ref": "#/definitions/codersdk.ProvisionerJob" }, "reason": { - "type": "string" + "type": "string", + "enum": ["initiator", "autostart", "autostop"] }, "resources": { "type": "array", diff --git a/codersdk/provisionerdaemons.go b/codersdk/provisionerdaemons.go index 18a771f41809b..da0d2b67d0910 100644 --- a/codersdk/provisionerdaemons.go +++ b/codersdk/provisionerdaemons.go @@ -66,24 +66,26 @@ const ( ProvisionerJobFailed ProvisionerJobStatus = "failed" ) +// ProvisionerJob describes the job executed by the provisioning daemon. type ProvisionerJob struct { - ID uuid.UUID `json:"id"` - CreatedAt time.Time `json:"created_at"` - StartedAt *time.Time `json:"started_at,omitempty"` - CompletedAt *time.Time `json:"completed_at,omitempty"` - CanceledAt *time.Time `json:"canceled_at,omitempty"` + ID uuid.UUID `json:"id" format:"uuid"` + CreatedAt time.Time `json:"created_at" format:"date-time"` + StartedAt *time.Time `json:"started_at,omitempty" format:"date-time"` + CompletedAt *time.Time `json:"completed_at,omitempty" format:"date-time"` + CanceledAt *time.Time `json:"canceled_at,omitempty" format:"date-time"` Error string `json:"error,omitempty"` - Status ProvisionerJobStatus `json:"status"` - WorkerID *uuid.UUID `json:"worker_id,omitempty"` - FileID uuid.UUID `json:"file_id"` + Status ProvisionerJobStatus `json:"status" enums:"pending,running,succeeded,canceling,canceled,failed"` + WorkerID *uuid.UUID `json:"worker_id,omitempty" format:"uuid"` + FileID uuid.UUID `json:"file_id" format:"uuid"` Tags map[string]string `json:"tags"` } +// ProvisionerJobLog represents the provisioner log entry annotated with source and level. type ProvisionerJobLog struct { ID int64 `json:"id"` - CreatedAt time.Time `json:"created_at"` + CreatedAt time.Time `json:"created_at" format:"date-time"` Source LogSource `json:"log_source"` - Level LogLevel `json:"log_level"` + Level LogLevel `json:"log_level" enums:"trace,debug,info,warn,error"` Stage string `json:"stage"` Output string `json:"output"` } diff --git a/codersdk/workspacebuilds.go b/codersdk/workspacebuilds.go index 5281710a797b0..cef31019aac7c 100644 --- a/codersdk/workspacebuilds.go +++ b/codersdk/workspacebuilds.go @@ -65,13 +65,15 @@ type WorkspaceBuild struct { InitiatorID uuid.UUID `json:"initiator_id" format:"uuid"` InitiatorUsername string `json:"initiator_name"` Job ProvisionerJob `json:"job"` - Reason BuildReason `db:"reason" json:"reason"` + Reason BuildReason `db:"reason" json:"reason" enums:"initiator,autostart,autostop"` Resources []WorkspaceResource `json:"resources"` Deadline NullTime `json:"deadline,omitempty" swaggertype:"string" format:"date-time"` Status WorkspaceStatus `json:"status" enums:"pending,starting,running,stopping,stopped,failed,canceling,canceled,deleting,deleted"` DailyCost int32 `json:"daily_cost"` } +// WorkspaceResource describes resources used to create a workspace, for instance: +// containers, images, volumes. type WorkspaceResource struct { ID uuid.UUID `json:"id" format:"uuid"` CreatedAt time.Time `json:"created_at" format:"date-time"` @@ -86,6 +88,7 @@ type WorkspaceResource struct { DailyCost int32 `json:"daily_cost"` } +// WorkspaceResourceMetadata annotates the workspace resource with custom key-value pairs. type WorkspaceResourceMetadata struct { Key string `json:"key"` Value string `json:"value"` diff --git a/docs/api/schemas.md b/docs/api/schemas.md index dddd6804a3ec0..7b4f90b58ad2c 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2267,19 +2267,19 @@ Parameter represents a set value for the scope. ```json { - "canceled_at": "string", - "completed_at": "string", - "created_at": "string", + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", "error": "string", - "file_id": "string", - "id": "string", - "started_at": "string", - "status": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", "tags": { "property1": "string", "property2": "string" }, - "worker_id": "string" + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" } ``` @@ -2299,13 +2299,24 @@ Parameter represents a set value for the scope. | » `[any property]` | string | false | | | | `worker_id` | string | false | | | +#### Enumerated Values + +| Property | Value | +| -------- | ----------- | +| `status` | `pending` | +| `status` | `running` | +| `status` | `succeeded` | +| `status` | `canceling` | +| `status` | `canceled` | +| `status` | `failed` | + ## codersdk.ProvisionerJobLog ```json { - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "id": 0, - "log_level": "string", + "log_level": "trace", "log_source": "string", "output": "string", "stage": "string" @@ -2323,6 +2334,16 @@ Parameter represents a set value for the scope. | `output` | string | false | | | | `stage` | string | false | | | +#### Enumerated Values + +| Property | Value | +| ----------- | ------- | +| `log_level` | `trace` | +| `log_level` | `debug` | +| `log_level` | `info` | +| `log_level` | `warn` | +| `log_level` | `error` | + ## codersdk.PutExtendWorkspaceRequest ```json @@ -2862,21 +2883,21 @@ Parameter represents a set value for the scope. "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", "job": { - "canceled_at": "string", - "completed_at": "string", - "created_at": "string", + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", "error": "string", - "file_id": "string", - "id": "string", - "started_at": "string", - "status": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", "tags": { "property1": "string", "property2": "string" }, - "worker_id": "string" + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" }, - "reason": "string", + "reason": "initiator", "resources": [ { "agents": [ @@ -3130,21 +3151,21 @@ Parameter represents a set value for the scope. "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", "job": { - "canceled_at": "string", - "completed_at": "string", - "created_at": "string", + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", "error": "string", - "file_id": "string", - "id": "string", - "started_at": "string", - "status": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", "tags": { "property1": "string", "property2": "string" }, - "worker_id": "string" + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" }, - "reason": "string", + "reason": "initiator", "resources": [ { "agents": [ @@ -3259,6 +3280,9 @@ Parameter represents a set value for the scope. | Property | Value | | ------------ | ----------- | +| `reason` | `initiator` | +| `reason` | `autostart` | +| `reason` | `autostop` | | `status` | `pending` | | `status` | `starting` | | `status` | `running` | @@ -3412,21 +3436,21 @@ Parameter represents a set value for the scope. "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", "job": { - "canceled_at": "string", - "completed_at": "string", - "created_at": "string", + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", "error": "string", - "file_id": "string", - "id": "string", - "started_at": "string", - "status": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", "tags": { "property1": "string", "property2": "string" }, - "worker_id": "string" + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" }, - "reason": "string", + "reason": "initiator", "resources": [ { "agents": [ diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 0bd455c5ef8d1..018936efd2851 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -41,21 +41,21 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", "job": { - "canceled_at": "string", - "completed_at": "string", - "created_at": "string", + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", "error": "string", - "file_id": "string", - "id": "string", - "started_at": "string", - "status": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", "tags": { "property1": "string", "property2": "string" }, - "worker_id": "string" + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" }, - "reason": "string", + "reason": "initiator", "resources": [ { "agents": [ @@ -202,21 +202,21 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", "job": { - "canceled_at": "string", - "completed_at": "string", - "created_at": "string", + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", "error": "string", - "file_id": "string", - "id": "string", - "started_at": "string", - "status": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", "tags": { "property1": "string", "property2": "string" }, - "worker_id": "string" + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" }, - "reason": "string", + "reason": "initiator", "resources": [ { "agents": [ @@ -356,21 +356,21 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", "job": { - "canceled_at": "string", - "completed_at": "string", - "created_at": "string", + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", "error": "string", - "file_id": "string", - "id": "string", - "started_at": "string", - "status": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", "tags": { "property1": "string", "property2": "string" }, - "worker_id": "string" + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" }, - "reason": "string", + "reason": "initiator", "resources": [ { "agents": [ @@ -538,9 +538,9 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/log ```json [ { - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "id": 0, - "log_level": "string", + "log_level": "trace", "log_source": "string", "output": "string", "stage": "string" @@ -568,6 +568,16 @@ Status Code **200** | `» output` | string | false | | | | `» stage` | string | false | | | +#### Enumerated Values + +| Property | Value | +| ----------- | ------- | +| `log_level` | `trace` | +| `log_level` | `debug` | +| `log_level` | `info` | +| `log_level` | `warn` | +| `log_level` | `error` | + To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. ## Get workspace resources for workspace build @@ -779,21 +789,21 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", "job": { - "canceled_at": "string", - "completed_at": "string", - "created_at": "string", + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", "error": "string", - "file_id": "string", - "id": "string", - "started_at": "string", - "status": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", "tags": { "property1": "string", "property2": "string" }, - "worker_id": "string" + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" }, - "reason": "string", + "reason": "initiator", "resources": [ { "agents": [ @@ -951,21 +961,21 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", "job": { - "canceled_at": "string", - "completed_at": "string", - "created_at": "string", + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", "error": "string", - "file_id": "string", - "id": "string", - "started_at": "string", - "status": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", "tags": { "property1": "string", "property2": "string" }, - "worker_id": "string" + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" }, - "reason": "string", + "reason": "initiator", "resources": [ { "agents": [ @@ -1109,21 +1119,21 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{id} \ "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "initiator_name": "string", "job": { - "canceled_at": "string", - "completed_at": "string", - "created_at": "string", + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", "error": "string", - "file_id": "string", - "id": "string", - "started_at": "string", - "status": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", "tags": { "property1": "string", "property2": "string" }, - "worker_id": "string" + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" }, - "reason": "string", + "reason": "initiator", "resources": [ { "agents": [ From 663ec87a0e00bbb01f6428a43e961ef929d56de9 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 2 Jan 2023 15:12:24 +0100 Subject: [PATCH 04/61] workspaceagents: instance identity --- coderd/apidoc/docs.go | 157 ++++++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 137 ++++++++++++++++++++++++++++ coderd/workspaceresourceauth.go | 27 ++++++ docs/api/schemas.md | 60 ++++++++++++ docs/api/workspaces.md | 140 ++++++++++++++++++++++++++++ 5 files changed, 521 insertions(+) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index a4fd764ceba6a..710633ba96e48 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -875,6 +875,114 @@ const docTemplate = `{ } } }, + "/workspaceagents/aws-instance-identity": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Workspaces" + ], + "summary": "Authenticate agent on AWS instance", + "operationId": "authenticate-agent-on-aws-instance", + "parameters": [ + { + "description": "Instance identity token", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AWSInstanceIdentityToken" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentAuthenticateResponse" + } + } + } + } + }, + "/workspaceagents/azure-instance-identity": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Workspaces" + ], + "summary": "Authenticate agent on Azure instance", + "operationId": "authenticate-agent-on-azure-instance", + "parameters": [ + { + "description": "Instance identity token", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AzureInstanceIdentityToken" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentAuthenticateResponse" + } + } + } + } + }, + "/workspaceagents/google-instance-identity": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Workspaces" + ], + "summary": "Authenticate agent on Google Cloud instance", + "operationId": "authenticate-agent-on-google-cloud-instance", + "parameters": [ + { + "description": "Instance identity token", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.GoogleInstanceIdentityToken" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentAuthenticateResponse" + } + } + } + } + }, "/workspacebuilds/{workspacebuild}": { "get": { "security": [ @@ -1409,6 +1517,21 @@ const docTemplate = `{ } } }, + "codersdk.AWSInstanceIdentityToken": { + "type": "object", + "required": [ + "document", + "signature" + ], + "properties": { + "document": { + "type": "string" + }, + "signature": { + "type": "string" + } + } + }, "codersdk.AuditDiff": { "type": "object", "additionalProperties": { @@ -1561,6 +1684,21 @@ const docTemplate = `{ "type": "boolean" } }, + "codersdk.AzureInstanceIdentityToken": { + "type": "object", + "required": [ + "encoding", + "signature" + ], + "properties": { + "encoding": { + "type": "string" + }, + "signature": { + "type": "string" + } + } + }, "codersdk.BuildInfoResponse": { "type": "object", "properties": { @@ -2095,6 +2233,17 @@ const docTemplate = `{ } } }, + "codersdk.GoogleInstanceIdentityToken": { + "type": "object", + "required": [ + "json_web_token" + ], + "properties": { + "json_web_token": { + "type": "string" + } + } + }, "codersdk.Healthcheck": { "type": "object", "properties": { @@ -2777,6 +2926,14 @@ const docTemplate = `{ } } }, + "codersdk.WorkspaceAgentAuthenticateResponse": { + "type": "object", + "properties": { + "session_token": { + "type": "string" + } + } + }, "codersdk.WorkspaceApp": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 7e12c5beb8c1f..67065b7260f6d 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -757,6 +757,102 @@ } } }, + "/workspaceagents/aws-instance-identity": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Workspaces"], + "summary": "Authenticate agent on AWS instance", + "operationId": "authenticate-agent-on-aws-instance", + "parameters": [ + { + "description": "Instance identity token", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AWSInstanceIdentityToken" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentAuthenticateResponse" + } + } + } + } + }, + "/workspaceagents/azure-instance-identity": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Workspaces"], + "summary": "Authenticate agent on Azure instance", + "operationId": "authenticate-agent-on-azure-instance", + "parameters": [ + { + "description": "Instance identity token", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AzureInstanceIdentityToken" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentAuthenticateResponse" + } + } + } + } + }, + "/workspaceagents/google-instance-identity": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Workspaces"], + "summary": "Authenticate agent on Google Cloud instance", + "operationId": "authenticate-agent-on-google-cloud-instance", + "parameters": [ + { + "description": "Instance identity token", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.GoogleInstanceIdentityToken" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentAuthenticateResponse" + } + } + } + } + }, "/workspacebuilds/{workspacebuild}": { "get": { "security": [ @@ -1230,6 +1326,18 @@ } } }, + "codersdk.AWSInstanceIdentityToken": { + "type": "object", + "required": ["document", "signature"], + "properties": { + "document": { + "type": "string" + }, + "signature": { + "type": "string" + } + } + }, "codersdk.AuditDiff": { "type": "object", "additionalProperties": { @@ -1377,6 +1485,18 @@ "type": "boolean" } }, + "codersdk.AzureInstanceIdentityToken": { + "type": "object", + "required": ["encoding", "signature"], + "properties": { + "encoding": { + "type": "string" + }, + "signature": { + "type": "string" + } + } + }, "codersdk.BuildInfoResponse": { "type": "object", "properties": { @@ -1895,6 +2015,15 @@ } } }, + "codersdk.GoogleInstanceIdentityToken": { + "type": "object", + "required": ["json_web_token"], + "properties": { + "json_web_token": { + "type": "string" + } + } + }, "codersdk.Healthcheck": { "type": "object", "properties": { @@ -2550,6 +2679,14 @@ } } }, + "codersdk.WorkspaceAgentAuthenticateResponse": { + "type": "object", + "properties": { + "session_token": { + "type": "string" + } + } + }, "codersdk.WorkspaceApp": { "type": "object", "properties": { diff --git a/coderd/workspaceresourceauth.go b/coderd/workspaceresourceauth.go index 84923fd33db00..396d324ff6389 100644 --- a/coderd/workspaceresourceauth.go +++ b/coderd/workspaceresourceauth.go @@ -19,6 +19,15 @@ import ( // Azure supports instance identity verification: // https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux#tabgroup_14 +// +// @Summary Authenticate agent on Azure instance +// @ID authenticate-agent-on-azure-instance +// @Security CoderSessionToken +// @Produce json +// @Tags Workspaces +// @Param request body codersdk.AzureInstanceIdentityToken true "Instance identity token" +// @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse +// @Router /workspaceagents/azure-instance-identity [post] func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var req codersdk.AzureInstanceIdentityToken @@ -39,6 +48,15 @@ func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r // AWS supports instance identity verification: // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html // Using this, we can exchange a signed instance payload for an agent token. +// +// @Summary Authenticate agent on AWS instance +// @ID authenticate-agent-on-aws-instance +// @Security CoderSessionToken +// @Produce json +// @Tags Workspaces +// @Param request body codersdk.AWSInstanceIdentityToken true "Instance identity token" +// @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse +// @Router /workspaceagents/aws-instance-identity [post] func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var req codersdk.AWSInstanceIdentityToken @@ -59,6 +77,15 @@ func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r * // Google Compute Engine supports instance identity verification: // https://cloud.google.com/compute/docs/instances/verifying-instance-identity // Using this, we can exchange a signed instance payload for an agent token. +// +// @Summary Authenticate agent on Google Cloud instance +// @ID authenticate-agent-on-google-cloud-instance +// @Security CoderSessionToken +// @Produce json +// @Tags Workspaces +// @Param request body codersdk.GoogleInstanceIdentityToken true "Instance identity token" +// @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse +// @Router /workspaceagents/google-instance-identity [post] func (api *API) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var req codersdk.GoogleInstanceIdentityToken diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 7b4f90b58ad2c..f7bf566b09612 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -16,6 +16,22 @@ | ------------ | ------ | -------- | ------------ | ----------- | | `csp-report` | object | false | | | +## codersdk.AWSInstanceIdentityToken + +```json +{ + "document": "string", + "signature": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------- | ------ | -------- | ------------ | ----------- | +| `document` | string | true | | | +| `signature` | string | true | | | + ## codersdk.AuditDiff ```json @@ -297,6 +313,22 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | ---------------- | ------- | -------- | ------------ | ----------- | | `[any property]` | boolean | false | | | +## codersdk.AzureInstanceIdentityToken + +```json +{ + "encoding": "string", + "signature": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------- | ------ | -------- | ------------ | ----------- | +| `encoding` | string | true | | | +| `signature` | string | true | | | + ## codersdk.BuildInfoResponse ```json @@ -1786,6 +1818,20 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `type` | string | false | | | | `validate_url` | string | false | | | +## codersdk.GoogleInstanceIdentityToken + +```json +{ + "json_web_token": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------------- | ------ | -------- | ------------ | ----------- | +| `json_web_token` | string | true | | | + ## codersdk.Healthcheck ```json @@ -3101,6 +3147,20 @@ Parameter represents a set value for the scope. | `updated_at` | string | false | | | | `version` | string | false | | | +## codersdk.WorkspaceAgentAuthenticateResponse + +```json +{ + "session_token": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| --------------- | ------ | -------- | ------------ | ----------- | +| `session_token` | string | false | | | + ## codersdk.WorkspaceApp ```json diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 018936efd2851..2e0ae40e7ff9e 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -323,6 +323,146 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. +## Authenticate agent on AWS instance + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/workspaceagents/aws-instance-identity \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /workspaceagents/aws-instance-identity` + +> Body parameter + +```json +{ + "document": "string", + "signature": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | -------------------------------------------------------------------------------- | -------- | ----------------------- | +| `body` | body | [codersdk.AWSInstanceIdentityToken](schemas.md#codersdkawsinstanceidentitytoken) | true | Instance identity token | + +### Example responses + +> 200 Response + +```json +{ + "session_token": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentAuthenticateResponse](schemas.md#codersdkworkspaceagentauthenticateresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Authenticate agent on Azure instance + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/workspaceagents/azure-instance-identity \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /workspaceagents/azure-instance-identity` + +> Body parameter + +```json +{ + "encoding": "string", + "signature": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------------------------------------------------------------------------------------ | -------- | ----------------------- | +| `body` | body | [codersdk.AzureInstanceIdentityToken](schemas.md#codersdkazureinstanceidentitytoken) | true | Instance identity token | + +### Example responses + +> 200 Response + +```json +{ + "session_token": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentAuthenticateResponse](schemas.md#codersdkworkspaceagentauthenticateresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Authenticate agent on Google Cloud instance + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/workspaceagents/google-instance-identity \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /workspaceagents/google-instance-identity` + +> Body parameter + +```json +{ + "json_web_token": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | -------------------------------------------------------------------------------------- | -------- | ----------------------- | +| `body` | body | [codersdk.GoogleInstanceIdentityToken](schemas.md#codersdkgoogleinstanceidentitytoken) | true | Instance identity token | + +### Example responses + +> 200 Response + +```json +{ + "session_token": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentAuthenticateResponse](schemas.md#codersdkworkspaceagentauthenticateresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + ## Get workspace build ### Code samples From fc00d7e10aec7f6b5f87b369f490fa4d4065b605 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 2 Jan 2023 15:44:40 +0100 Subject: [PATCH 05/61] workspaceagents.go in progress --- coderd/apidoc/docs.go | 210 ++++++++++++++++-- coderd/apidoc/swagger.json | 195 +++++++++++++++-- coderd/workspaceagents.go | 8 + coderd/workspaceresourceauth.go | 6 +- codersdk/workspaceagents.go | 16 +- codersdk/workspaceapps.go | 4 +- docs/api/agents.md | 252 ++++++++++++++++++++++ docs/api/schemas.md | 370 +++++++++++++++++++++++++++----- docs/api/workspaces.md | 297 +++++++------------------ docs/manifest.json | 4 + 10 files changed, 1056 insertions(+), 306 deletions(-) create mode 100644 docs/api/agents.md diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 710633ba96e48..b8180c96821b4 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -886,7 +886,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Workspaces" + "Agents" ], "summary": "Authenticate agent on AWS instance", "operationId": "authenticate-agent-on-aws-instance", @@ -922,7 +922,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Workspaces" + "Agents" ], "summary": "Authenticate agent on Azure instance", "operationId": "authenticate-agent-on-azure-instance", @@ -958,7 +958,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Workspaces" + "Agents" ], "summary": "Authenticate agent on Google Cloud instance", "operationId": "authenticate-agent-on-google-cloud-instance", @@ -983,6 +983,34 @@ const docTemplate = `{ } } }, + "/workspaceagents/me/metadata": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Agents" + ], + "summary": "Get authorized workspace agent metadata", + "operationId": "get-authorized-workspace-agent-metadata", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentMetadata" + } + } + } + } + }, "/workspacebuilds/{workspacebuild}": { "get": { "security": [ @@ -2867,13 +2895,15 @@ const docTemplate = `{ "type": "integer" }, "created_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "directory": { "type": "string" }, "disconnected_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "environment_variables": { "type": "object", @@ -2882,16 +2912,19 @@ const docTemplate = `{ } }, "first_connected_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "instance_id": { "type": "string" }, "last_connected_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "latency": { "description": "DERPLatency is mapped by region name (e.g. \"New York City\", \"Seattle\").", @@ -2907,19 +2940,27 @@ const docTemplate = `{ "type": "string" }, "resource_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "startup_script": { "type": "string" }, "status": { - "type": "string" + "type": "string", + "enum": [ + "connecting", + "connected", + "disconnected", + "timeout" + ] }, "troubleshooting_url": { "type": "string" }, "updated_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "version": { "type": "string" @@ -2934,6 +2975,42 @@ const docTemplate = `{ } } }, + "codersdk.WorkspaceAgentMetadata": { + "type": "object", + "properties": { + "apps": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceApp" + } + }, + "derpmap": { + "$ref": "#/definitions/tailcfg.DERPMap" + }, + "directory": { + "type": "string" + }, + "environment_variables": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "git_auth_configs": { + "description": "GitAuthConfigs stores the number of Git configurations\nthe Coder deployment has. If this number is \u003e0, we\nset up special configuration in the workspace.", + "type": "integer" + }, + "motd_file": { + "type": "string" + }, + "startup_script": { + "type": "string" + }, + "vscode_port_proxy_uri": { + "type": "string" + } + } + }, "codersdk.WorkspaceApp": { "type": "object", "properties": { @@ -2960,10 +3037,16 @@ const docTemplate = `{ "type": "string" }, "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "sharing_level": { - "type": "string" + "type": "string", + "enum": [ + "owner", + "authenticated", + "public" + ] }, "slug": { "description": "Slug is a unique identifier within the agent.", @@ -3156,6 +3239,107 @@ const docTemplate = `{ }, "netip.Addr": { "type": "object" + }, + "tailcfg.DERPMap": { + "type": "object", + "properties": { + "omitDefaultRegions": { + "description": "OmitDefaultRegions specifies to not use Tailscale's DERP servers, and only use those\nspecified in this DERPMap. If there are none set outside of the defaults, this is a noop.", + "type": "boolean" + }, + "regions": { + "description": "Regions is the set of geographic regions running DERP node(s).\n\nIt's keyed by the DERPRegion.RegionID.\n\nThe numbers are not necessarily contiguous.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/tailcfg.DERPRegion" + } + } + } + }, + "tailcfg.DERPNode": { + "type": "object", + "properties": { + "certName": { + "description": "CertName optionally specifies the expected TLS cert common\nname. If empty, HostName is used. If CertName is non-empty,\nHostName is only used for the TCP dial (if IPv4/IPv6 are\nnot present) + TLS ClientHello.", + "type": "string" + }, + "derpport": { + "description": "DERPPort optionally provides an alternate TLS port number\nfor the DERP HTTPS server.\n\nIf zero, 443 is used.", + "type": "integer" + }, + "forceHTTP": { + "description": "ForceHTTP is used by unit tests to force HTTP.\nIt should not be set by users.", + "type": "boolean" + }, + "hostName": { + "description": "HostName is the DERP node's hostname.\n\nIt is required but need not be unique; multiple nodes may\nhave the same HostName but vary in configuration otherwise.", + "type": "string" + }, + "insecureForTests": { + "description": "InsecureForTests is used by unit tests to disable TLS verification.\nIt should not be set by users.", + "type": "boolean" + }, + "ipv4": { + "description": "IPv4 optionally forces an IPv4 address to use, instead of using DNS.\nIf empty, A record(s) from DNS lookups of HostName are used.\nIf the string is not an IPv4 address, IPv4 is not used; the\nconventional string to disable IPv4 (and not use DNS) is\n\"none\".", + "type": "string" + }, + "ipv6": { + "description": "IPv6 optionally forces an IPv6 address to use, instead of using DNS.\nIf empty, AAAA record(s) from DNS lookups of HostName are used.\nIf the string is not an IPv6 address, IPv6 is not used; the\nconventional string to disable IPv6 (and not use DNS) is\n\"none\".", + "type": "string" + }, + "name": { + "description": "Name is a unique node name (across all regions).\nIt is not a host name.\nIt's typically of the form \"1b\", \"2a\", \"3b\", etc. (region\nID + suffix within that region)", + "type": "string" + }, + "regionID": { + "description": "RegionID is the RegionID of the DERPRegion that this node\nis running in.", + "type": "integer" + }, + "stunonly": { + "description": "STUNOnly marks a node as only a STUN server and not a DERP\nserver.", + "type": "boolean" + }, + "stunport": { + "description": "Port optionally specifies a STUN port to use.\nZero means 3478.\nTo disable STUN on this node, use -1.", + "type": "integer" + }, + "stuntestIP": { + "description": "STUNTestIP is used in tests to override the STUN server's IP.\nIf empty, it's assumed to be the same as the DERP server.", + "type": "string" + } + } + }, + "tailcfg.DERPRegion": { + "type": "object", + "properties": { + "avoid": { + "description": "Avoid is whether the client should avoid picking this as its home\nregion. The region should only be used if a peer is there.\nClients already using this region as their home should migrate\naway to a new region without Avoid set.", + "type": "boolean" + }, + "embeddedRelay": { + "description": "EmbeddedRelay is true when the region is bundled with the Coder\ncontrol plane.", + "type": "boolean" + }, + "nodes": { + "description": "Nodes are the DERP nodes running in this region, in\npriority order for the current client. Client TLS\nconnections should ideally only go to the first entry\n(falling back to the second if necessary). STUN packets\nshould go to the first 1 or 2.\n\nIf nodes within a region route packets amongst themselves,\nbut not to other regions. That said, each user/domain\nshould get a the same preferred node order, so if all nodes\nfor a user/network pick the first one (as they should, when\nthings are healthy), the inter-cluster routing is minimal\nto zero.", + "type": "array", + "items": { + "$ref": "#/definitions/tailcfg.DERPNode" + } + }, + "regionCode": { + "description": "RegionCode is a short name for the region. It's usually a popular\ncity or airport code in the region: \"nyc\", \"sf\", \"sin\",\n\"fra\", etc.", + "type": "string" + }, + "regionID": { + "description": "RegionID is a unique integer for a geographic region.\n\nIt corresponds to the legacy derpN.tailscale.com hostnames\nused by older clients. (Older clients will continue to resolve\nderpN.tailscale.com when contacting peers, rather than use\nthe server-provided DERPMap)\n\nRegionIDs must be non-zero, positive, and guaranteed to fit\nin a JavaScript number.\n\nRegionIDs in range 900-999 are reserved for end users to run their\nown DERP nodes.", + "type": "integer" + }, + "regionName": { + "description": "RegionName is a long English name for the region: \"New York City\",\n\"San Francisco\", \"Singapore\", \"Frankfurt\", etc.", + "type": "string" + } + } } }, "securityDefinitions": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 67065b7260f6d..4ddae97c7e83a 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -765,7 +765,7 @@ } ], "produces": ["application/json"], - "tags": ["Workspaces"], + "tags": ["Agents"], "summary": "Authenticate agent on AWS instance", "operationId": "authenticate-agent-on-aws-instance", "parameters": [ @@ -797,7 +797,7 @@ } ], "produces": ["application/json"], - "tags": ["Workspaces"], + "tags": ["Agents"], "summary": "Authenticate agent on Azure instance", "operationId": "authenticate-agent-on-azure-instance", "parameters": [ @@ -829,7 +829,7 @@ } ], "produces": ["application/json"], - "tags": ["Workspaces"], + "tags": ["Agents"], "summary": "Authenticate agent on Google Cloud instance", "operationId": "authenticate-agent-on-google-cloud-instance", "parameters": [ @@ -853,6 +853,28 @@ } } }, + "/workspaceagents/me/metadata": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Agents"], + "summary": "Get authorized workspace agent metadata", + "operationId": "get-authorized-workspace-agent-metadata", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentMetadata" + } + } + } + } + }, "/workspacebuilds/{workspacebuild}": { "get": { "security": [ @@ -2620,13 +2642,15 @@ "type": "integer" }, "created_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "directory": { "type": "string" }, "disconnected_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "environment_variables": { "type": "object", @@ -2635,16 +2659,19 @@ } }, "first_connected_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "instance_id": { "type": "string" }, "last_connected_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "latency": { "description": "DERPLatency is mapped by region name (e.g. \"New York City\", \"Seattle\").", @@ -2660,19 +2687,22 @@ "type": "string" }, "resource_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "startup_script": { "type": "string" }, "status": { - "type": "string" + "type": "string", + "enum": ["connecting", "connected", "disconnected", "timeout"] }, "troubleshooting_url": { "type": "string" }, "updated_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "version": { "type": "string" @@ -2687,6 +2717,42 @@ } } }, + "codersdk.WorkspaceAgentMetadata": { + "type": "object", + "properties": { + "apps": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceApp" + } + }, + "derpmap": { + "$ref": "#/definitions/tailcfg.DERPMap" + }, + "directory": { + "type": "string" + }, + "environment_variables": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "git_auth_configs": { + "description": "GitAuthConfigs stores the number of Git configurations\nthe Coder deployment has. If this number is \u003e0, we\nset up special configuration in the workspace.", + "type": "integer" + }, + "motd_file": { + "type": "string" + }, + "startup_script": { + "type": "string" + }, + "vscode_port_proxy_uri": { + "type": "string" + } + } + }, "codersdk.WorkspaceApp": { "type": "object", "properties": { @@ -2713,10 +2779,12 @@ "type": "string" }, "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "sharing_level": { - "type": "string" + "type": "string", + "enum": ["owner", "authenticated", "public"] }, "slug": { "description": "Slug is a unique identifier within the agent.", @@ -2897,6 +2965,107 @@ }, "netip.Addr": { "type": "object" + }, + "tailcfg.DERPMap": { + "type": "object", + "properties": { + "omitDefaultRegions": { + "description": "OmitDefaultRegions specifies to not use Tailscale's DERP servers, and only use those\nspecified in this DERPMap. If there are none set outside of the defaults, this is a noop.", + "type": "boolean" + }, + "regions": { + "description": "Regions is the set of geographic regions running DERP node(s).\n\nIt's keyed by the DERPRegion.RegionID.\n\nThe numbers are not necessarily contiguous.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/tailcfg.DERPRegion" + } + } + } + }, + "tailcfg.DERPNode": { + "type": "object", + "properties": { + "certName": { + "description": "CertName optionally specifies the expected TLS cert common\nname. If empty, HostName is used. If CertName is non-empty,\nHostName is only used for the TCP dial (if IPv4/IPv6 are\nnot present) + TLS ClientHello.", + "type": "string" + }, + "derpport": { + "description": "DERPPort optionally provides an alternate TLS port number\nfor the DERP HTTPS server.\n\nIf zero, 443 is used.", + "type": "integer" + }, + "forceHTTP": { + "description": "ForceHTTP is used by unit tests to force HTTP.\nIt should not be set by users.", + "type": "boolean" + }, + "hostName": { + "description": "HostName is the DERP node's hostname.\n\nIt is required but need not be unique; multiple nodes may\nhave the same HostName but vary in configuration otherwise.", + "type": "string" + }, + "insecureForTests": { + "description": "InsecureForTests is used by unit tests to disable TLS verification.\nIt should not be set by users.", + "type": "boolean" + }, + "ipv4": { + "description": "IPv4 optionally forces an IPv4 address to use, instead of using DNS.\nIf empty, A record(s) from DNS lookups of HostName are used.\nIf the string is not an IPv4 address, IPv4 is not used; the\nconventional string to disable IPv4 (and not use DNS) is\n\"none\".", + "type": "string" + }, + "ipv6": { + "description": "IPv6 optionally forces an IPv6 address to use, instead of using DNS.\nIf empty, AAAA record(s) from DNS lookups of HostName are used.\nIf the string is not an IPv6 address, IPv6 is not used; the\nconventional string to disable IPv6 (and not use DNS) is\n\"none\".", + "type": "string" + }, + "name": { + "description": "Name is a unique node name (across all regions).\nIt is not a host name.\nIt's typically of the form \"1b\", \"2a\", \"3b\", etc. (region\nID + suffix within that region)", + "type": "string" + }, + "regionID": { + "description": "RegionID is the RegionID of the DERPRegion that this node\nis running in.", + "type": "integer" + }, + "stunonly": { + "description": "STUNOnly marks a node as only a STUN server and not a DERP\nserver.", + "type": "boolean" + }, + "stunport": { + "description": "Port optionally specifies a STUN port to use.\nZero means 3478.\nTo disable STUN on this node, use -1.", + "type": "integer" + }, + "stuntestIP": { + "description": "STUNTestIP is used in tests to override the STUN server's IP.\nIf empty, it's assumed to be the same as the DERP server.", + "type": "string" + } + } + }, + "tailcfg.DERPRegion": { + "type": "object", + "properties": { + "avoid": { + "description": "Avoid is whether the client should avoid picking this as its home\nregion. The region should only be used if a peer is there.\nClients already using this region as their home should migrate\naway to a new region without Avoid set.", + "type": "boolean" + }, + "embeddedRelay": { + "description": "EmbeddedRelay is true when the region is bundled with the Coder\ncontrol plane.", + "type": "boolean" + }, + "nodes": { + "description": "Nodes are the DERP nodes running in this region, in\npriority order for the current client. Client TLS\nconnections should ideally only go to the first entry\n(falling back to the second if necessary). STUN packets\nshould go to the first 1 or 2.\n\nIf nodes within a region route packets amongst themselves,\nbut not to other regions. That said, each user/domain\nshould get a the same preferred node order, so if all nodes\nfor a user/network pick the first one (as they should, when\nthings are healthy), the inter-cluster routing is minimal\nto zero.", + "type": "array", + "items": { + "$ref": "#/definitions/tailcfg.DERPNode" + } + }, + "regionCode": { + "description": "RegionCode is a short name for the region. It's usually a popular\ncity or airport code in the region: \"nyc\", \"sf\", \"sin\",\n\"fra\", etc.", + "type": "string" + }, + "regionID": { + "description": "RegionID is a unique integer for a geographic region.\n\nIt corresponds to the legacy derpN.tailscale.com hostnames\nused by older clients. (Older clients will continue to resolve\nderpN.tailscale.com when contacting peers, rather than use\nthe server-provided DERPMap)\n\nRegionIDs must be non-zero, positive, and guaranteed to fit\nin a JavaScript number.\n\nRegionIDs in range 900-999 are reserved for end users to run their\nown DERP nodes.", + "type": "integer" + }, + "regionName": { + "description": "RegionName is a long English name for the region: \"New York City\",\n\"San Francisco\", \"Singapore\", \"Frankfurt\", etc.", + "type": "string" + } + } } }, "securityDefinitions": { diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 649d1849df2c9..3330559e6d132 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -65,6 +65,14 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, apiAgent) } +// @Summary Get authorized workspace agent metadata +// @ID get-authorized-workspace-agent-metadata +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Agents +// @Success 200 {object} codersdk.WorkspaceAgentMetadata +// @Router /workspaceagents/me/metadata [get] func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgent(r) diff --git a/coderd/workspaceresourceauth.go b/coderd/workspaceresourceauth.go index 396d324ff6389..ea72b794ada37 100644 --- a/coderd/workspaceresourceauth.go +++ b/coderd/workspaceresourceauth.go @@ -24,7 +24,7 @@ import ( // @ID authenticate-agent-on-azure-instance // @Security CoderSessionToken // @Produce json -// @Tags Workspaces +// @Tags Agents // @Param request body codersdk.AzureInstanceIdentityToken true "Instance identity token" // @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse // @Router /workspaceagents/azure-instance-identity [post] @@ -53,7 +53,7 @@ func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r // @ID authenticate-agent-on-aws-instance // @Security CoderSessionToken // @Produce json -// @Tags Workspaces +// @Tags Agents // @Param request body codersdk.AWSInstanceIdentityToken true "Instance identity token" // @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse // @Router /workspaceagents/aws-instance-identity [post] @@ -82,7 +82,7 @@ func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r * // @ID authenticate-agent-on-google-cloud-instance // @Security CoderSessionToken // @Produce json -// @Tags Workspaces +// @Tags Agents // @Param request body codersdk.GoogleInstanceIdentityToken true "Instance identity token" // @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse // @Router /workspaceagents/google-instance-identity [post] diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 258b108737b9c..cd4c237616607 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -35,15 +35,15 @@ const ( ) type WorkspaceAgent struct { - ID uuid.UUID `json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - FirstConnectedAt *time.Time `json:"first_connected_at,omitempty"` - LastConnectedAt *time.Time `json:"last_connected_at,omitempty"` - DisconnectedAt *time.Time `json:"disconnected_at,omitempty"` - Status WorkspaceAgentStatus `json:"status"` + ID uuid.UUID `json:"id" format:"uuid"` + CreatedAt time.Time `json:"created_at" format:"date-time"` + UpdatedAt time.Time `json:"updated_at" format:"date-time"` + FirstConnectedAt *time.Time `json:"first_connected_at,omitempty" format:"date-time"` + LastConnectedAt *time.Time `json:"last_connected_at,omitempty" format:"date-time"` + DisconnectedAt *time.Time `json:"disconnected_at,omitempty" format:"date-time"` + Status WorkspaceAgentStatus `json:"status" enums:"connecting,connected,disconnected,timeout"` Name string `json:"name"` - ResourceID uuid.UUID `json:"resource_id"` + ResourceID uuid.UUID `json:"resource_id" format:"uuid"` InstanceID string `json:"instance_id,omitempty"` Architecture string `json:"architecture"` EnvironmentVariables map[string]string `json:"environment_variables"` diff --git a/codersdk/workspaceapps.go b/codersdk/workspaceapps.go index 8f6003b5fae48..a8bcb31b792d7 100644 --- a/codersdk/workspaceapps.go +++ b/codersdk/workspaceapps.go @@ -22,7 +22,7 @@ const ( ) type WorkspaceApp struct { - ID uuid.UUID `json:"id"` + ID uuid.UUID `json:"id" format:"uuid"` // URL is the address being proxied to inside the workspace. // If external is specified, this will be opened on the client. URL string `json:"url"` @@ -42,7 +42,7 @@ type WorkspaceApp struct { // and there is no app wildcard configured on the server, the app will not // be accessible in the UI. Subdomain bool `json:"subdomain"` - SharingLevel WorkspaceAppSharingLevel `json:"sharing_level"` + SharingLevel WorkspaceAppSharingLevel `json:"sharing_level" enums:"owner,authenticated,public"` // Healthcheck specifies the configuration for checking app health. Healthcheck Healthcheck `json:"healthcheck"` Health WorkspaceAppHealth `json:"health"` diff --git a/docs/api/agents.md b/docs/api/agents.md new file mode 100644 index 0000000000000..f04a8a5ff15ef --- /dev/null +++ b/docs/api/agents.md @@ -0,0 +1,252 @@ +# Agents + +> This page is incomplete, stay tuned. + +## Authenticate agent on AWS instance + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/workspaceagents/aws-instance-identity \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /workspaceagents/aws-instance-identity` + +> Body parameter + +```json +{ + "document": "string", + "signature": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | -------------------------------------------------------------------------------- | -------- | ----------------------- | +| `body` | body | [codersdk.AWSInstanceIdentityToken](schemas.md#codersdkawsinstanceidentitytoken) | true | Instance identity token | + +### Example responses + +> 200 Response + +```json +{ + "session_token": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentAuthenticateResponse](schemas.md#codersdkworkspaceagentauthenticateresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Authenticate agent on Azure instance + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/workspaceagents/azure-instance-identity \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /workspaceagents/azure-instance-identity` + +> Body parameter + +```json +{ + "encoding": "string", + "signature": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------------------------------------------------------------------------------------ | -------- | ----------------------- | +| `body` | body | [codersdk.AzureInstanceIdentityToken](schemas.md#codersdkazureinstanceidentitytoken) | true | Instance identity token | + +### Example responses + +> 200 Response + +```json +{ + "session_token": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentAuthenticateResponse](schemas.md#codersdkworkspaceagentauthenticateresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Authenticate agent on Google Cloud instance + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/workspaceagents/google-instance-identity \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /workspaceagents/google-instance-identity` + +> Body parameter + +```json +{ + "json_web_token": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | -------------------------------------------------------------------------------------- | -------- | ----------------------- | +| `body` | body | [codersdk.GoogleInstanceIdentityToken](schemas.md#codersdkgoogleinstanceidentitytoken) | true | Instance identity token | + +### Example responses + +> 200 Response + +```json +{ + "session_token": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentAuthenticateResponse](schemas.md#codersdkworkspaceagentauthenticateresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get authorized workspace agent metadata + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/metadata \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspaceagents/me/metadata` + +### Example responses + +> 200 Response + +```json +{ + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "derpmap": { + "omitDefaultRegions": true, + "regions": { + "property1": { + "avoid": true, + "embeddedRelay": true, + "nodes": [ + { + "certName": "string", + "derpport": 0, + "forceHTTP": true, + "hostName": "string", + "insecureForTests": true, + "ipv4": "string", + "ipv6": "string", + "name": "string", + "regionID": 0, + "stunonly": true, + "stunport": 0, + "stuntestIP": "string" + } + ], + "regionCode": "string", + "regionID": 0, + "regionName": "string" + }, + "property2": { + "avoid": true, + "embeddedRelay": true, + "nodes": [ + { + "certName": "string", + "derpport": 0, + "forceHTTP": true, + "hostName": "string", + "insecureForTests": true, + "ipv4": "string", + "ipv6": "string", + "name": "string", + "regionID": 0, + "stunonly": true, + "stunport": 0, + "stuntestIP": "string" + } + ], + "regionCode": "string", + "regionID": 0, + "regionName": "string" + } + } + }, + "directory": "string", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "git_auth_configs": 0, + "motd_file": "string", + "startup_script": "string", + "vscode_port_proxy_uri": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentMetadata](schemas.md#codersdkworkspaceagentmetadata) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/api/schemas.md b/docs/api/schemas.md index f7bf566b09612..d01652d468e97 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2960,8 +2960,8 @@ Parameter represents a set value for the scope. "url": "string" }, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -2969,17 +2969,17 @@ Parameter represents a set value for the scope. ], "architecture": "string", "connection_timeout_seconds": 0, - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "directory": "string", - "disconnected_at": "string", + "disconnected_at": "2019-08-24T14:15:22Z", "environment_variables": { "property1": "string", "property2": "string" }, - "first_connected_at": "string", - "id": "string", + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "instance_id": "string", - "last_connected_at": "string", + "last_connected_at": "2019-08-24T14:15:22Z", "latency": { "property1": { "latency_ms": 0, @@ -2992,11 +2992,11 @@ Parameter represents a set value for the scope. }, "name": "string", "operating_system": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "startup_script": "string", - "status": "string", + "status": "connecting", "troubleshooting_url": "string", - "updated_at": "string", + "updated_at": "2019-08-24T14:15:22Z", "version": "string" } ], @@ -3079,8 +3079,8 @@ Parameter represents a set value for the scope. "url": "string" }, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -3088,17 +3088,17 @@ Parameter represents a set value for the scope. ], "architecture": "string", "connection_timeout_seconds": 0, - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "directory": "string", - "disconnected_at": "string", + "disconnected_at": "2019-08-24T14:15:22Z", "environment_variables": { "property1": "string", "property2": "string" }, - "first_connected_at": "string", - "id": "string", + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "instance_id": "string", - "last_connected_at": "string", + "last_connected_at": "2019-08-24T14:15:22Z", "latency": { "property1": { "latency_ms": 0, @@ -3111,11 +3111,11 @@ Parameter represents a set value for the scope. }, "name": "string", "operating_system": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "startup_script": "string", - "status": "string", + "status": "connecting", "troubleshooting_url": "string", - "updated_at": "string", + "updated_at": "2019-08-24T14:15:22Z", "version": "string" } ``` @@ -3147,6 +3147,15 @@ Parameter represents a set value for the scope. | `updated_at` | string | false | | | | `version` | string | false | | | +#### Enumerated Values + +| Property | Value | +| -------- | -------------- | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | + ## codersdk.WorkspaceAgentAuthenticateResponse ```json @@ -3161,6 +3170,106 @@ Parameter represents a set value for the scope. | --------------- | ------ | -------- | ------------ | ----------- | | `session_token` | string | false | | | +## codersdk.WorkspaceAgentMetadata + +```json +{ + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "derpmap": { + "omitDefaultRegions": true, + "regions": { + "property1": { + "avoid": true, + "embeddedRelay": true, + "nodes": [ + { + "certName": "string", + "derpport": 0, + "forceHTTP": true, + "hostName": "string", + "insecureForTests": true, + "ipv4": "string", + "ipv6": "string", + "name": "string", + "regionID": 0, + "stunonly": true, + "stunport": 0, + "stuntestIP": "string" + } + ], + "regionCode": "string", + "regionID": 0, + "regionName": "string" + }, + "property2": { + "avoid": true, + "embeddedRelay": true, + "nodes": [ + { + "certName": "string", + "derpport": 0, + "forceHTTP": true, + "hostName": "string", + "insecureForTests": true, + "ipv4": "string", + "ipv6": "string", + "name": "string", + "regionID": 0, + "stunonly": true, + "stunport": 0, + "stuntestIP": "string" + } + ], + "regionCode": "string", + "regionID": 0, + "regionName": "string" + } + } + }, + "directory": "string", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "git_auth_configs": 0, + "motd_file": "string", + "startup_script": "string", + "vscode_port_proxy_uri": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------------------- | ------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `apps` | array of [codersdk.WorkspaceApp](#codersdkworkspaceapp) | false | | | +| `derpmap` | [tailcfg.DERPMap](#tailcfgderpmap) | false | | | +| `directory` | string | false | | | +| `environment_variables` | object | false | | | +| » `[any property]` | string | false | | | +| `git_auth_configs` | integer | false | | Git auth configs stores the number of Git configurations the Coder deployment has. If this number is >0, we set up special configuration in the workspace. | +| `motd_file` | string | false | | | +| `startup_script` | string | false | | | +| `vscode_port_proxy_uri` | string | false | | | + ## codersdk.WorkspaceApp ```json @@ -3175,8 +3284,8 @@ Parameter represents a set value for the scope. "url": "string" }, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -3199,6 +3308,14 @@ Parameter represents a set value for the scope. | `subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | | `url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +#### Enumerated Values + +| Property | Value | +| --------------- | --------------- | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | + ## codersdk.WorkspaceBuild ```json @@ -3242,8 +3359,8 @@ Parameter represents a set value for the scope. "url": "string" }, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -3251,17 +3368,17 @@ Parameter represents a set value for the scope. ], "architecture": "string", "connection_timeout_seconds": 0, - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "directory": "string", - "disconnected_at": "string", + "disconnected_at": "2019-08-24T14:15:22Z", "environment_variables": { "property1": "string", "property2": "string" }, - "first_connected_at": "string", - "id": "string", + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "instance_id": "string", - "last_connected_at": "string", + "last_connected_at": "2019-08-24T14:15:22Z", "latency": { "property1": { "latency_ms": 0, @@ -3274,11 +3391,11 @@ Parameter represents a set value for the scope. }, "name": "string", "operating_system": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "startup_script": "string", - "status": "string", + "status": "connecting", "troubleshooting_url": "string", - "updated_at": "string", + "updated_at": "2019-08-24T14:15:22Z", "version": "string" } ], @@ -3375,8 +3492,8 @@ Parameter represents a set value for the scope. "url": "string" }, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -3384,17 +3501,17 @@ Parameter represents a set value for the scope. ], "architecture": "string", "connection_timeout_seconds": 0, - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "directory": "string", - "disconnected_at": "string", + "disconnected_at": "2019-08-24T14:15:22Z", "environment_variables": { "property1": "string", "property2": "string" }, - "first_connected_at": "string", - "id": "string", + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "instance_id": "string", - "last_connected_at": "string", + "last_connected_at": "2019-08-24T14:15:22Z", "latency": { "property1": { "latency_ms": 0, @@ -3407,11 +3524,11 @@ Parameter represents a set value for the scope. }, "name": "string", "operating_system": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "startup_script": "string", - "status": "string", + "status": "connecting", "troubleshooting_url": "string", - "updated_at": "string", + "updated_at": "2019-08-24T14:15:22Z", "version": "string" } ], @@ -3523,8 +3640,8 @@ Parameter represents a set value for the scope. "health": "string", "healthcheck": {}, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -3532,17 +3649,17 @@ Parameter represents a set value for the scope. ], "architecture": "string", "connection_timeout_seconds": 0, - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "directory": "string", - "disconnected_at": "string", + "disconnected_at": "2019-08-24T14:15:22Z", "environment_variables": { "property1": "string", "property2": "string" }, - "first_connected_at": "string", - "id": "string", + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "instance_id": "string", - "last_connected_at": "string", + "last_connected_at": "2019-08-24T14:15:22Z", "latency": { "property1": { "latency_ms": 0, @@ -3555,11 +3672,11 @@ Parameter represents a set value for the scope. }, "name": "string", "operating_system": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "startup_script": "string", - "status": "string", + "status": "connecting", "troubleshooting_url": "string", - "updated_at": "string", + "updated_at": "2019-08-24T14:15:22Z", "version": "string" } ], @@ -3623,3 +3740,152 @@ Parameter represents a set value for the scope. ### Properties _None_ + +## tailcfg.DERPMap + +```json +{ + "omitDefaultRegions": true, + "regions": { + "property1": { + "avoid": true, + "embeddedRelay": true, + "nodes": [ + { + "certName": "string", + "derpport": 0, + "forceHTTP": true, + "hostName": "string", + "insecureForTests": true, + "ipv4": "string", + "ipv6": "string", + "name": "string", + "regionID": 0, + "stunonly": true, + "stunport": 0, + "stuntestIP": "string" + } + ], + "regionCode": "string", + "regionID": 0, + "regionName": "string" + }, + "property2": { + "avoid": true, + "embeddedRelay": true, + "nodes": [ + { + "certName": "string", + "derpport": 0, + "forceHTTP": true, + "hostName": "string", + "insecureForTests": true, + "ipv4": "string", + "ipv6": "string", + "name": "string", + "regionID": 0, + "stunonly": true, + "stunport": 0, + "stuntestIP": "string" + } + ], + "regionCode": "string", + "regionID": 0, + "regionName": "string" + } + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------------------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `omitDefaultRegions` | boolean | false | | Omitdefaultregions specifies to not use Tailscale's DERP servers, and only use those specified in this DERPMap. If there are none set outside of the defaults, this is a noop. | +| `regions` | object | false | | Regions is the set of geographic regions running DERP node(s). | + +It's keyed by the DERPRegion.RegionID. +The numbers are not necessarily contiguous.| +|» `[any property]`|[tailcfg.DERPRegion](#tailcfgderpregion)|false||| + +## tailcfg.DERPNode + +```json +{ + "certName": "string", + "derpport": 0, + "forceHTTP": true, + "hostName": "string", + "insecureForTests": true, + "ipv4": "string", + "ipv6": "string", + "name": "string", + "regionID": 0, + "stunonly": true, + "stunport": 0, + "stuntestIP": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| --------------------------------------------------------------------------------------------------------------------- | ------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `certName` | string | false | | Certname optionally specifies the expected TLS cert common name. If empty, HostName is used. If CertName is non-empty, HostName is only used for the TCP dial (if IPv4/IPv6 are not present) + TLS ClientHello. | +| `derpport` | integer | false | | Derpport optionally provides an alternate TLS port number for the DERP HTTPS server. | +| If zero, 443 is used. | +| `forceHTTP` | boolean | false | | Forcehttp is used by unit tests to force HTTP. It should not be set by users. | +| `hostName` | string | false | | Hostname is the DERP node's hostname. | +| It is required but need not be unique; multiple nodes may have the same HostName but vary in configuration otherwise. | +| `insecureForTests` | boolean | false | | Insecurefortests is used by unit tests to disable TLS verification. It should not be set by users. | +| `ipv4` | string | false | | Ipv4 optionally forces an IPv4 address to use, instead of using DNS. If empty, A record(s) from DNS lookups of HostName are used. If the string is not an IPv4 address, IPv4 is not used; the conventional string to disable IPv4 (and not use DNS) is "none". | +| `ipv6` | string | false | | Ipv6 optionally forces an IPv6 address to use, instead of using DNS. If empty, AAAA record(s) from DNS lookups of HostName are used. If the string is not an IPv6 address, IPv6 is not used; the conventional string to disable IPv6 (and not use DNS) is "none". | +| `name` | string | false | | Name is a unique node name (across all regions). It is not a host name. It's typically of the form "1b", "2a", "3b", etc. (region ID + suffix within that region) | +| `regionID` | integer | false | | Regionid is the RegionID of the DERPRegion that this node is running in. | +| `stunonly` | boolean | false | | Stunonly marks a node as only a STUN server and not a DERP server. | +| `stunport` | integer | false | | Port optionally specifies a STUN port to use. Zero means 3478. To disable STUN on this node, use -1. | +| `stuntestIP` | string | false | | Stuntestip is used in tests to override the STUN server's IP. If empty, it's assumed to be the same as the DERP server. | + +## tailcfg.DERPRegion + +```json +{ + "avoid": true, + "embeddedRelay": true, + "nodes": [ + { + "certName": "string", + "derpport": 0, + "forceHTTP": true, + "hostName": "string", + "insecureForTests": true, + "ipv4": "string", + "ipv6": "string", + "name": "string", + "regionID": 0, + "stunonly": true, + "stunport": 0, + "stuntestIP": "string" + } + ], + "regionCode": "string", + "regionID": 0, + "regionName": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `avoid` | boolean | false | | Avoid is whether the client should avoid picking this as its home region. The region should only be used if a peer is there. Clients already using this region as their home should migrate away to a new region without Avoid set. | +| `embeddedRelay` | boolean | false | | Embeddedrelay is true when the region is bundled with the Coder control plane. | +| `nodes` | array of [tailcfg.DERPNode](#tailcfgderpnode) | false | | Nodes are the DERP nodes running in this region, in priority order for the current client. Client TLS connections should ideally only go to the first entry (falling back to the second if necessary). STUN packets should go to the first 1 or 2. | +| If nodes within a region route packets amongst themselves, but not to other regions. That said, each user/domain should get a the same preferred node order, so if all nodes for a user/network pick the first one (as they should, when things are healthy), the inter-cluster routing is minimal to zero. | +| `regionCode` | string | false | | Regioncode is a short name for the region. It's usually a popular city or airport code in the region: "nyc", "sf", "sin", "fra", etc. | +| `regionID` | integer | false | | Regionid is a unique integer for a geographic region. | + +It corresponds to the legacy derpN.tailscale.com hostnames used by older clients. (Older clients will continue to resolve derpN.tailscale.com when contacting peers, rather than use the server-provided DERPMap) +RegionIDs must be non-zero, positive, and guaranteed to fit in a JavaScript number. +RegionIDs in range 900-999 are reserved for end users to run their own DERP nodes.| +|`regionName`|string|false||Regionname is a long English name for the region: "New York City", "San Francisco", "Singapore", "Frankfurt", etc.| diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 2e0ae40e7ff9e..288465fcab3e5 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -72,8 +72,8 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member "url": "string" }, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -81,17 +81,17 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member ], "architecture": "string", "connection_timeout_seconds": 0, - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "directory": "string", - "disconnected_at": "string", + "disconnected_at": "2019-08-24T14:15:22Z", "environment_variables": { "property1": "string", "property2": "string" }, - "first_connected_at": "string", - "id": "string", + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "instance_id": "string", - "last_connected_at": "string", + "last_connected_at": "2019-08-24T14:15:22Z", "latency": { "property1": { "latency_ms": 0, @@ -104,11 +104,11 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member }, "name": "string", "operating_system": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "startup_script": "string", - "status": "string", + "status": "connecting", "troubleshooting_url": "string", - "updated_at": "string", + "updated_at": "2019-08-24T14:15:22Z", "version": "string" } ], @@ -233,8 +233,8 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "url": "string" }, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -242,17 +242,17 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam ], "architecture": "string", "connection_timeout_seconds": 0, - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "directory": "string", - "disconnected_at": "string", + "disconnected_at": "2019-08-24T14:15:22Z", "environment_variables": { "property1": "string", "property2": "string" }, - "first_connected_at": "string", - "id": "string", + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "instance_id": "string", - "last_connected_at": "string", + "last_connected_at": "2019-08-24T14:15:22Z", "latency": { "property1": { "latency_ms": 0, @@ -265,11 +265,11 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam }, "name": "string", "operating_system": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "startup_script": "string", - "status": "string", + "status": "connecting", "troubleshooting_url": "string", - "updated_at": "string", + "updated_at": "2019-08-24T14:15:22Z", "version": "string" } ], @@ -323,146 +323,6 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. -## Authenticate agent on AWS instance - -### Code samples - -```shell -# Example request using curl -curl -X POST http://coder-server:8080/api/v2/workspaceagents/aws-instance-identity \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`POST /workspaceagents/aws-instance-identity` - -> Body parameter - -```json -{ - "document": "string", - "signature": "string" -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -| ------ | ---- | -------------------------------------------------------------------------------- | -------- | ----------------------- | -| `body` | body | [codersdk.AWSInstanceIdentityToken](schemas.md#codersdkawsinstanceidentitytoken) | true | Instance identity token | - -### Example responses - -> 200 Response - -```json -{ - "session_token": "string" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentAuthenticateResponse](schemas.md#codersdkworkspaceagentauthenticateresponse) | - -To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. - -## Authenticate agent on Azure instance - -### Code samples - -```shell -# Example request using curl -curl -X POST http://coder-server:8080/api/v2/workspaceagents/azure-instance-identity \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`POST /workspaceagents/azure-instance-identity` - -> Body parameter - -```json -{ - "encoding": "string", - "signature": "string" -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -| ------ | ---- | ------------------------------------------------------------------------------------ | -------- | ----------------------- | -| `body` | body | [codersdk.AzureInstanceIdentityToken](schemas.md#codersdkazureinstanceidentitytoken) | true | Instance identity token | - -### Example responses - -> 200 Response - -```json -{ - "session_token": "string" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentAuthenticateResponse](schemas.md#codersdkworkspaceagentauthenticateresponse) | - -To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. - -## Authenticate agent on Google Cloud instance - -### Code samples - -```shell -# Example request using curl -curl -X POST http://coder-server:8080/api/v2/workspaceagents/google-instance-identity \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`POST /workspaceagents/google-instance-identity` - -> Body parameter - -```json -{ - "json_web_token": "string" -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -| ------ | ---- | -------------------------------------------------------------------------------------- | -------- | ----------------------- | -| `body` | body | [codersdk.GoogleInstanceIdentityToken](schemas.md#codersdkgoogleinstanceidentitytoken) | true | Instance identity token | - -### Example responses - -> 200 Response - -```json -{ - "session_token": "string" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentAuthenticateResponse](schemas.md#codersdkworkspaceagentauthenticateresponse) | - -To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. - ## Get workspace build ### Code samples @@ -527,8 +387,8 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ "url": "string" }, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -536,17 +396,17 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ ], "architecture": "string", "connection_timeout_seconds": 0, - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "directory": "string", - "disconnected_at": "string", + "disconnected_at": "2019-08-24T14:15:22Z", "environment_variables": { "property1": "string", "property2": "string" }, - "first_connected_at": "string", - "id": "string", + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "instance_id": "string", - "last_connected_at": "string", + "last_connected_at": "2019-08-24T14:15:22Z", "latency": { "property1": { "latency_ms": 0, @@ -559,11 +419,11 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ }, "name": "string", "operating_system": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "startup_script": "string", - "status": "string", + "status": "connecting", "troubleshooting_url": "string", - "updated_at": "string", + "updated_at": "2019-08-24T14:15:22Z", "version": "string" } ], @@ -760,8 +620,8 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res "url": "string" }, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -769,17 +629,17 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res ], "architecture": "string", "connection_timeout_seconds": 0, - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "directory": "string", - "disconnected_at": "string", + "disconnected_at": "2019-08-24T14:15:22Z", "environment_variables": { "property1": "string", "property2": "string" }, - "first_connected_at": "string", - "id": "string", + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "instance_id": "string", - "last_connected_at": "string", + "last_connected_at": "2019-08-24T14:15:22Z", "latency": { "property1": { "latency_ms": 0, @@ -792,11 +652,11 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res }, "name": "string", "operating_system": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "startup_script": "string", - "status": "string", + "status": "connecting", "troubleshooting_url": "string", - "updated_at": "string", + "updated_at": "2019-08-24T14:15:22Z", "version": "string" } ], @@ -888,11 +748,18 @@ Status Code **200** #### Enumerated Values -| Property | Value | -| ---------------------- | -------- | -| `workspace_transition` | `start` | -| `workspace_transition` | `stop` | -| `workspace_transition` | `delete` | +| Property | Value | +| ---------------------- | --------------- | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. @@ -960,8 +827,8 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta "url": "string" }, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -969,17 +836,17 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta ], "architecture": "string", "connection_timeout_seconds": 0, - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "directory": "string", - "disconnected_at": "string", + "disconnected_at": "2019-08-24T14:15:22Z", "environment_variables": { "property1": "string", "property2": "string" }, - "first_connected_at": "string", - "id": "string", + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "instance_id": "string", - "last_connected_at": "string", + "last_connected_at": "2019-08-24T14:15:22Z", "latency": { "property1": { "latency_ms": 0, @@ -992,11 +859,11 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta }, "name": "string", "operating_system": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "startup_script": "string", - "status": "string", + "status": "connecting", "troubleshooting_url": "string", - "updated_at": "string", + "updated_at": "2019-08-24T14:15:22Z", "version": "string" } ], @@ -1128,8 +995,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "health": "string", "healthcheck": {}, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -1137,17 +1004,17 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ ], "architecture": "string", "connection_timeout_seconds": 0, - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "directory": "string", - "disconnected_at": "string", + "disconnected_at": "2019-08-24T14:15:22Z", "environment_variables": { "property1": "string", "property2": "string" }, - "first_connected_at": "string", - "id": "string", + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "instance_id": "string", - "last_connected_at": "string", + "last_connected_at": "2019-08-24T14:15:22Z", "latency": { "property1": { "latency_ms": 0, @@ -1160,11 +1027,11 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ }, "name": "string", "operating_system": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "startup_script": "string", - "status": "string", + "status": "connecting", "troubleshooting_url": "string", - "updated_at": "string", + "updated_at": "2019-08-24T14:15:22Z", "version": "string" } ], @@ -1290,8 +1157,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{id} \ "url": "string" }, "icon": "string", - "id": "string", - "sharing_level": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", "slug": "string", "subdomain": true, "url": "string" @@ -1299,17 +1166,17 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{id} \ ], "architecture": "string", "connection_timeout_seconds": 0, - "created_at": "string", + "created_at": "2019-08-24T14:15:22Z", "directory": "string", - "disconnected_at": "string", + "disconnected_at": "2019-08-24T14:15:22Z", "environment_variables": { "property1": "string", "property2": "string" }, - "first_connected_at": "string", - "id": "string", + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "instance_id": "string", - "last_connected_at": "string", + "last_connected_at": "2019-08-24T14:15:22Z", "latency": { "property1": { "latency_ms": 0, @@ -1322,11 +1189,11 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{id} \ }, "name": "string", "operating_system": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "startup_script": "string", - "status": "string", + "status": "connecting", "troubleshooting_url": "string", - "updated_at": "string", + "updated_at": "2019-08-24T14:15:22Z", "version": "string" } ], diff --git a/docs/manifest.json b/docs/manifest.json index 5e20465ae272e..ee27d96a9c510 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -361,6 +361,10 @@ "title": "Parameters", "path": "./api/parameters.md" }, + { + "title": "Agents", + "path": "./api/agents.md" + }, { "title": "Schemas", "path": "./api/schemas.md" From 67a85b93f42d4aeefcb7f01c7ade6699db773f44 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 2 Jan 2023 16:10:29 +0100 Subject: [PATCH 06/61] workspaceagents.go in progress --- coderd/apidoc/docs.go | 221 +++++++++++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 203 ++++++++++++++++++++++++++++++++++ coderd/workspaceagents.go | 37 ++++++- docs/api/agents.md | 167 ++++++++++++++++++++++++++++ docs/api/schemas.md | 92 +++++++++++++++ 5 files changed, 719 insertions(+), 1 deletion(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index b8180c96821b4..939d03ef63286 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -983,6 +983,83 @@ const docTemplate = `{ } } }, + "/workspaceagents/me/app-health": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Agents" + ], + "summary": "Submit workspace application health", + "operationId": "submit-workspace-workspace-agent-health", + "parameters": [ + { + "description": "Application health request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.PostWorkspaceAppHealthsRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/workspaceagents/me/gitauth": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Agents" + ], + "summary": "Get workspace agent Git auth", + "operationId": "get-workspace-agent-git-auth", + "parameters": [ + { + "type": "string", + "format": "uri", + "description": "Git URL", + "name": "url", + "in": "query", + "required": true + }, + { + "type": "boolean", + "description": "Wait for a new token to be issued", + "name": "listen", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentGitAuthResponse" + } + } + } + } + }, "/workspaceagents/me/metadata": { "get": { "security": [ @@ -1011,6 +1088,75 @@ const docTemplate = `{ } } }, + "/workspaceagents/me/report-stats": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Agents" + ], + "summary": "Submit workspace agent stats", + "operationId": "submit-workspace-workspace-agent-stats", + "parameters": [ + { + "description": "Stats request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AgentStats" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AgentStatsResponse" + } + } + } + } + }, + "/workspaceagents/me/version": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Agents" + ], + "summary": "Submit workspace agent version", + "operationId": "submit-workspace-workspace-agent-version", + "parameters": [ + { + "description": "Version request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.PostWorkspaceAgentVersionRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/workspacebuilds/{workspacebuild}": { "get": { "security": [ @@ -1560,6 +1706,47 @@ const docTemplate = `{ } } }, + "codersdk.AgentStats": { + "type": "object", + "properties": { + "conns_by_proto": { + "description": "ConnsByProto is a count of connections by protocol.", + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "num_comms": { + "description": "NumConns is the number of connections received by an agent.", + "type": "integer" + }, + "rx_bytes": { + "description": "RxBytes is the number of received bytes.", + "type": "integer" + }, + "rx_packets": { + "description": "RxPackets is the number of received packets.", + "type": "integer" + }, + "tx_bytes": { + "description": "TxBytes is the number of transmitted bytes.", + "type": "integer" + }, + "tx_packets": { + "description": "TxPackets is the number of transmitted bytes.", + "type": "integer" + } + } + }, + "codersdk.AgentStatsResponse": { + "type": "object", + "properties": { + "report_interval": { + "description": "ReportInterval is the duration after which the agent should send stats\nagain.", + "type": "integer" + } + } + }, "codersdk.AuditDiff": { "type": "object", "additionalProperties": { @@ -2395,6 +2582,26 @@ const docTemplate = `{ } } }, + "codersdk.PostWorkspaceAgentVersionRequest": { + "type": "object", + "properties": { + "version": { + "type": "string" + } + } + }, + "codersdk.PostWorkspaceAppHealthsRequest": { + "type": "object", + "properties": { + "healths": { + "description": "Healths is a map of the workspace app name and the health of the app.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, "codersdk.PprofConfig": { "type": "object", "properties": { @@ -2975,6 +3182,20 @@ const docTemplate = `{ } } }, + "codersdk.WorkspaceAgentGitAuthResponse": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "url": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "codersdk.WorkspaceAgentMetadata": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 4ddae97c7e83a..dd59178737b2f 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -853,6 +853,73 @@ } } }, + "/workspaceagents/me/app-health": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Agents"], + "summary": "Submit workspace application health", + "operationId": "submit-workspace-workspace-agent-health", + "parameters": [ + { + "description": "Application health request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.PostWorkspaceAppHealthsRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/workspaceagents/me/gitauth": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Agents"], + "summary": "Get workspace agent Git auth", + "operationId": "get-workspace-agent-git-auth", + "parameters": [ + { + "type": "string", + "format": "uri", + "description": "Git URL", + "name": "url", + "in": "query", + "required": true + }, + { + "type": "boolean", + "description": "Wait for a new token to be issued", + "name": "listen", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentGitAuthResponse" + } + } + } + } + }, "/workspaceagents/me/metadata": { "get": { "security": [ @@ -875,6 +942,67 @@ } } }, + "/workspaceagents/me/report-stats": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Agents"], + "summary": "Submit workspace agent stats", + "operationId": "submit-workspace-workspace-agent-stats", + "parameters": [ + { + "description": "Stats request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AgentStats" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AgentStatsResponse" + } + } + } + } + }, + "/workspaceagents/me/version": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Agents"], + "summary": "Submit workspace agent version", + "operationId": "submit-workspace-workspace-agent-version", + "parameters": [ + { + "description": "Version request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.PostWorkspaceAgentVersionRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/workspacebuilds/{workspacebuild}": { "get": { "security": [ @@ -1360,6 +1488,47 @@ } } }, + "codersdk.AgentStats": { + "type": "object", + "properties": { + "conns_by_proto": { + "description": "ConnsByProto is a count of connections by protocol.", + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "num_comms": { + "description": "NumConns is the number of connections received by an agent.", + "type": "integer" + }, + "rx_bytes": { + "description": "RxBytes is the number of received bytes.", + "type": "integer" + }, + "rx_packets": { + "description": "RxPackets is the number of received packets.", + "type": "integer" + }, + "tx_bytes": { + "description": "TxBytes is the number of transmitted bytes.", + "type": "integer" + }, + "tx_packets": { + "description": "TxPackets is the number of transmitted bytes.", + "type": "integer" + } + } + }, + "codersdk.AgentStatsResponse": { + "type": "object", + "properties": { + "report_interval": { + "description": "ReportInterval is the duration after which the agent should send stats\nagain.", + "type": "integer" + } + } + }, "codersdk.AuditDiff": { "type": "object", "additionalProperties": { @@ -2158,6 +2327,26 @@ } } }, + "codersdk.PostWorkspaceAgentVersionRequest": { + "type": "object", + "properties": { + "version": { + "type": "string" + } + } + }, + "codersdk.PostWorkspaceAppHealthsRequest": { + "type": "object", + "properties": { + "healths": { + "description": "Healths is a map of the workspace app name and the health of the app.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, "codersdk.PprofConfig": { "type": "object", "properties": { @@ -2717,6 +2906,20 @@ } } }, + "codersdk.WorkspaceAgentGitAuthResponse": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "url": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "codersdk.WorkspaceAgentMetadata": { "type": "object", "properties": { diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 3330559e6d132..c6cd768f40f3a 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -148,6 +148,14 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) }) } +// @Summary Submit workspace agent version +// @ID submit-workspace-workspace-agent-version +// @Security CoderSessionToken +// @Produce application/json +// @Tags Agents +// @Param request body codersdk.PostWorkspaceAgentVersionRequest true "Version request" +// @Success 200 +// @Router /workspaceagents/me/version [post] func (api *API) postWorkspaceAgentVersion(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgent(r) @@ -767,6 +775,14 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin return workspaceAgent, nil } +// @Summary Submit workspace agent stats +// @ID submit-workspace-workspace-agent-stats +// @Security CoderSessionToken +// @Produce application/json +// @Tags Agents +// @Param request body codersdk.AgentStats true "Stats request" +// @Success 200 {object} codersdk.AgentStatsResponse +// @Router /workspaceagents/me/report-stats [post] func (api *API) workspaceAgentReportStats(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -965,6 +981,14 @@ func (api *API) workspaceAgentReportStatsWebsocket(rw http.ResponseWriter, r *ht } } +// @Summary Submit workspace application health +// @ID submit-workspace-workspace-agent-health +// @Security CoderSessionToken +// @Produce application/json +// @Tags Agents +// @Param request body codersdk.PostWorkspaceAppHealthsRequest true "Application health request" +// @Success 200 +// @Router /workspaceagents/me/app-health [post] func (api *API) postWorkspaceAppHealth(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgent(r) @@ -1080,7 +1104,18 @@ func (api *API) postWorkspaceAppHealth(rw http.ResponseWriter, r *http.Request) httpapi.Write(ctx, rw, http.StatusOK, nil) } -// postWorkspaceAgentsGitAuth returns a username and password for use +// @Summary Get workspace agent Git auth +// @ID get-workspace-agent-git-auth +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Agents +// @Param url query string true "Git URL" format(uri) +// @Param listen query bool false "Wait for a new token to be issued" +// @Success 200 {object} codersdk.WorkspaceAgentGitAuthResponse +// @Router /workspaceagents/me/gitauth [get] +// +// workspaceAgentsGitAuth returns a username and password for use // with GIT_ASKPASS. func (api *API) workspaceAgentsGitAuth(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/docs/api/agents.md b/docs/api/agents.md index f04a8a5ff15ef..2f287e1903daf 100644 --- a/docs/api/agents.md +++ b/docs/api/agents.md @@ -142,6 +142,84 @@ curl -X POST http://coder-server:8080/api/v2/workspaceagents/google-instance-ide To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. +## Submit workspace application health + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/workspaceagents/me/app-health \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /workspaceagents/me/app-health` + +> Body parameter + +```json +{ + "healths": { + "property1": "string", + "property2": "string" + } +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | -------------------------------------------------------------------------------------------- | -------- | -------------------------- | +| `body` | body | [codersdk.PostWorkspaceAppHealthsRequest](schemas.md#codersdkpostworkspaceapphealthsrequest) | true | Application health request | + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get workspace agent Git auth + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/gitauth?url=http%3A%2F%2Fexample.com \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspaceagents/me/gitauth` + +### Parameters + +| Name | In | Type | Required | Description | +| -------- | ----- | ----------- | -------- | --------------------------------- | +| `url` | query | string(uri) | true | Git URL | +| `listen` | query | boolean | false | Wait for a new token to be issued | + +### Example responses + +> 200 Response + +```json +{ + "password": "string", + "url": "string", + "username": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentGitAuthResponse](schemas.md#codersdkworkspaceagentgitauthresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + ## Get authorized workspace agent metadata ### Code samples @@ -250,3 +328,92 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/metadata \ | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentMetadata](schemas.md#codersdkworkspaceagentmetadata) | To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Submit workspace agent stats + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/workspaceagents/me/report-stats \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /workspaceagents/me/report-stats` + +> Body parameter + +```json +{ + "conns_by_proto": { + "property1": 0, + "property2": 0 + }, + "num_comms": 0, + "rx_bytes": 0, + "rx_packets": 0, + "tx_bytes": 0, + "tx_packets": 0 +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ---------------------------------------------------- | -------- | ------------- | +| `body` | body | [codersdk.AgentStats](schemas.md#codersdkagentstats) | true | Stats request | + +### Example responses + +> 200 Response + +```json +{ + "report_interval": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.AgentStatsResponse](schemas.md#codersdkagentstatsresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Submit workspace agent version + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/workspaceagents/me/version \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /workspaceagents/me/version` + +> Body parameter + +```json +{ + "version": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------------------------------------------------------------------------------------------------ | -------- | --------------- | +| `body` | body | [codersdk.PostWorkspaceAgentVersionRequest](schemas.md#codersdkpostworkspaceagentversionrequest) | true | Version request | + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/api/schemas.md b/docs/api/schemas.md index d01652d468e97..be3449adadf1a 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -32,6 +32,48 @@ | `document` | string | true | | | | `signature` | string | true | | | +## codersdk.AgentStats + +```json +{ + "conns_by_proto": { + "property1": 0, + "property2": 0 + }, + "num_comms": 0, + "rx_bytes": 0, + "rx_packets": 0, + "tx_bytes": 0, + "tx_packets": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | ------- | -------- | ------------ | ------------------------------------------------------------ | +| `conns_by_proto` | object | false | | Conns by proto is a count of connections by protocol. | +| » `[any property]` | integer | false | | | +| `num_comms` | integer | false | | Num comms is the number of connections received by an agent. | +| `rx_bytes` | integer | false | | Rx bytes is the number of received bytes. | +| `rx_packets` | integer | false | | Rx packets is the number of received packets. | +| `tx_bytes` | integer | false | | Tx bytes is the number of transmitted bytes. | +| `tx_packets` | integer | false | | Tx packets is the number of transmitted bytes. | + +## codersdk.AgentStatsResponse + +```json +{ + "report_interval": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------ | +| `report_interval` | integer | false | | Report interval is the duration after which the agent should send stats again. | + ## codersdk.AuditDiff ```json @@ -2177,6 +2219,38 @@ Parameter represents a set value for the scope. | `source_scheme` | `none` | | `source_scheme` | `data` | +## codersdk.PostWorkspaceAgentVersionRequest + +```json +{ + "version": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| --------- | ------ | -------- | ------------ | ----------- | +| `version` | string | false | | | + +## codersdk.PostWorkspaceAppHealthsRequest + +```json +{ + "healths": { + "property1": "string", + "property2": "string" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | ------ | -------- | ------------ | --------------------------------------------------------------------- | +| `healths` | object | false | | Healths is a map of the workspace app name and the health of the app. | +| » `[any property]` | string | false | | | + ## codersdk.PprofConfig ```json @@ -3170,6 +3244,24 @@ Parameter represents a set value for the scope. | --------------- | ------ | -------- | ------------ | ----------- | | `session_token` | string | false | | | +## codersdk.WorkspaceAgentGitAuthResponse + +```json +{ + "password": "string", + "url": "string", + "username": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------- | ------ | -------- | ------------ | ----------- | +| `password` | string | false | | | +| `url` | string | false | | | +| `username` | string | false | | | + ## codersdk.WorkspaceAgentMetadata ```json From ce8c7eacbbc72c97b8e7db96cd4092495c30b08f Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 2 Jan 2023 16:38:18 +0100 Subject: [PATCH 07/61] Agents --- coderd/apidoc/docs.go | 62 ++++++++++++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 52 ++++++++++++++++++++++++++++++++ coderd/gitsshkey.go | 8 +++++ coderd/workspaceagents.go | 9 ++++++ docs/api/agents.md | 55 +++++++++++++++++++++++++++++++++ docs/api/schemas.md | 16 ++++++++++ 6 files changed, 202 insertions(+) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 939d03ef63286..15111a5a9aaba 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1016,6 +1016,29 @@ const docTemplate = `{ } } }, + "/workspaceagents/me/coordinate": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "description": "It accepts a WebSocket connection to an agent that listens to\nincoming connections and publishes node updates.", + "produces": [ + "application/json" + ], + "tags": [ + "Agents" + ], + "summary": "Coordinate workspace agent via Tailnet", + "operationId": "get-workspace-agent-git-ssh-key-via-tailnet", + "responses": { + "101": { + "description": "Switching Protocols" + } + } + } + }, "/workspaceagents/me/gitauth": { "get": { "security": [ @@ -1060,6 +1083,34 @@ const docTemplate = `{ } } }, + "/workspaceagents/me/gitsshkey": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Agents" + ], + "summary": "Get workspace agent Git SSH key", + "operationId": "get-workspace-agent-git-ssh-key", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AgentGitSSHKey" + } + } + } + } + }, "/workspaceagents/me/metadata": { "get": { "security": [ @@ -1706,6 +1757,17 @@ const docTemplate = `{ } } }, + "codersdk.AgentGitSSHKey": { + "type": "object", + "properties": { + "private_key": { + "type": "string" + }, + "public_key": { + "type": "string" + } + } + }, "codersdk.AgentStats": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index dd59178737b2f..e6307dd643d5c 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -882,6 +882,25 @@ } } }, + "/workspaceagents/me/coordinate": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "description": "It accepts a WebSocket connection to an agent that listens to\nincoming connections and publishes node updates.", + "produces": ["application/json"], + "tags": ["Agents"], + "summary": "Coordinate workspace agent via Tailnet", + "operationId": "get-workspace-agent-git-ssh-key-via-tailnet", + "responses": { + "101": { + "description": "Switching Protocols" + } + } + } + }, "/workspaceagents/me/gitauth": { "get": { "security": [ @@ -920,6 +939,28 @@ } } }, + "/workspaceagents/me/gitsshkey": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Agents"], + "summary": "Get workspace agent Git SSH key", + "operationId": "get-workspace-agent-git-ssh-key", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AgentGitSSHKey" + } + } + } + } + }, "/workspaceagents/me/metadata": { "get": { "security": [ @@ -1488,6 +1529,17 @@ } } }, + "codersdk.AgentGitSSHKey": { + "type": "object", + "properties": { + "private_key": { + "type": "string" + }, + "public_key": { + "type": "string" + } + } + }, "codersdk.AgentStats": { "type": "object", "properties": { diff --git a/coderd/gitsshkey.go b/coderd/gitsshkey.go index 357f5b2e44dab..a0f15da2e9e0c 100644 --- a/coderd/gitsshkey.go +++ b/coderd/gitsshkey.go @@ -100,6 +100,14 @@ func (api *API) gitSSHKey(rw http.ResponseWriter, r *http.Request) { }) } +// @Summary Get workspace agent Git SSH key +// @ID get-workspace-agent-git-ssh-key +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Agents +// @Success 200 {object} codersdk.AgentGitSSHKey +// @Router /workspaceagents/me/gitsshkey [get] func (api *API) agentGitSSHKey(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() agent := httpmw.WorkspaceAgent(r) diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index c6cd768f40f3a..ff8a23354e901 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -456,6 +456,15 @@ func (api *API) workspaceAgentConnection(rw http.ResponseWriter, r *http.Request }) } +// @Summary Coordinate workspace agent via Tailnet +// @Description It accepts a WebSocket connection to an agent that listens to +// @Description incoming connections and publishes node updates. +// @ID get-workspace-agent-git-ssh-key-via-tailnet +// @Security CoderSessionToken +// @Produce json +// @Tags Agents +// @Success 101 +// @Router /workspaceagents/me/coordinate [get] func (api *API) workspaceAgentCoordinate(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/docs/api/agents.md b/docs/api/agents.md index 2f287e1903daf..d001d5309d67c 100644 --- a/docs/api/agents.md +++ b/docs/api/agents.md @@ -180,6 +180,29 @@ curl -X POST http://coder-server:8080/api/v2/workspaceagents/me/app-health \ To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. +## Coordinate workspace agent via Tailnet + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/coordinate \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspaceagents/me/coordinate` + +It accepts a WebSocket connection to an agent that listens to +incoming connections and publishes node updates. + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------------------ | ------------------- | ------ | +| 101 | [Switching Protocols](https://tools.ietf.org/html/rfc7231#section-6.2.2) | Switching Protocols | | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + ## Get workspace agent Git auth ### Code samples @@ -220,6 +243,38 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/gitauth?url=http% To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. +## Get workspace agent Git SSH key + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/gitsshkey \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspaceagents/me/gitsshkey` + +### Example responses + +> 200 Response + +```json +{ + "private_key": "string", + "public_key": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.AgentGitSSHKey](schemas.md#codersdkagentgitsshkey) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + ## Get authorized workspace agent metadata ### Code samples diff --git a/docs/api/schemas.md b/docs/api/schemas.md index be3449adadf1a..843ed32642484 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -32,6 +32,22 @@ | `document` | string | true | | | | `signature` | string | true | | | +## codersdk.AgentGitSSHKey + +```json +{ + "private_key": "string", + "public_key": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------- | ------ | -------- | ------------ | ----------- | +| `private_key` | string | false | | | +| `public_key` | string | false | | | + ## codersdk.AgentStats ```json From d2a9af51afb44e592e9c92008fc335ce0663d7af Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 2 Jan 2023 17:17:14 +0100 Subject: [PATCH 08/61] workspacebuilds.go --- coderd/apidoc/docs.go | 10 +- coderd/apidoc/swagger.json | 10 +- coderd/workspacebuilds.go | 10 +- docs/api/builds.md | 585 +++++++++++++++++++++++++++++++++++++ docs/api/workspaces.md | 582 ------------------------------------ docs/manifest.json | 4 + 6 files changed, 604 insertions(+), 597 deletions(-) create mode 100644 docs/api/builds.md diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 15111a5a9aaba..09b3ccfbfc8fb 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1219,7 +1219,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Workspaces" + "Builds" ], "summary": "Get workspace build", "operationId": "get-workspace-build", @@ -1253,7 +1253,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Workspaces" + "Builds" ], "summary": "Cancel workspace build", "operationId": "cancel-workspace-build", @@ -1287,7 +1287,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Workspaces" + "Builds" ], "summary": "Get workspace build logs", "operationId": "get-workspace-build-logs", @@ -1342,7 +1342,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Workspaces" + "Builds" ], "summary": "Get workspace resources for workspace build", "operationId": "get-workspace-resources-for-workspace-build", @@ -1379,7 +1379,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Workspaces" + "Builds" ], "summary": "Get provisioner state for workspace build", "operationId": "get-provisioner-state-for-workspace-build", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index e6307dd643d5c..e93f620f9260c 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1052,7 +1052,7 @@ } ], "produces": ["application/json"], - "tags": ["Workspaces"], + "tags": ["Builds"], "summary": "Get workspace build", "operationId": "get-workspace-build", "parameters": [ @@ -1082,7 +1082,7 @@ } ], "produces": ["application/json"], - "tags": ["Workspaces"], + "tags": ["Builds"], "summary": "Cancel workspace build", "operationId": "cancel-workspace-build", "parameters": [ @@ -1112,7 +1112,7 @@ } ], "produces": ["application/json"], - "tags": ["Workspaces"], + "tags": ["Builds"], "summary": "Get workspace build logs", "operationId": "get-workspace-build-logs", "parameters": [ @@ -1163,7 +1163,7 @@ } ], "produces": ["application/json"], - "tags": ["Workspaces"], + "tags": ["Builds"], "summary": "Get workspace resources for workspace build", "operationId": "get-workspace-resources-for-workspace-build", "parameters": [ @@ -1196,7 +1196,7 @@ } ], "produces": ["application/json"], - "tags": ["Workspaces"], + "tags": ["Builds"], "summary": "Get provisioner state for workspace build", "operationId": "get-provisioner-state-for-workspace-build", "parameters": [ diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index eee08bad5ec0d..ef8f9b3503ba9 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -27,7 +27,7 @@ import ( // @ID get-workspace-build // @Security CoderSessionToken // @Produce json -// @Tags Workspaces +// @Tags Builds // @Param workspacebuild path string true "Workspace build ID" // @Success 200 {object} codersdk.WorkspaceBuild // @Router /workspacebuilds/{workspacebuild} [get] @@ -547,7 +547,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { // @ID cancel-workspace-build // @Security CoderSessionToken // @Produce json -// @Tags Workspaces +// @Tags Builds // @Param workspacebuild path string true "Workspace build ID" // @Success 200 {object} codersdk.Response // @Router /workspacebuilds/{workspacebuild}/cancel [patch] @@ -650,7 +650,7 @@ func (api *API) verifyUserCanCancelWorkspaceBuilds(ctx context.Context, userID u // @ID get-workspace-resources-for-workspace-build // @Security CoderSessionToken // @Produce json -// @Tags Workspaces +// @Tags Builds // @Param workspacebuild path string true "Workspace build ID" // @Success 200 {array} codersdk.WorkspaceResource // @Router /workspacebuilds/{workspacebuild}/resources [get] @@ -685,7 +685,7 @@ func (api *API) workspaceBuildResources(rw http.ResponseWriter, r *http.Request) // @ID get-workspace-build-logs // @Security CoderSessionToken // @Produce json -// @Tags Workspaces +// @Tags Builds // @Param workspacebuild path string true "Workspace build ID" // @Param before query int false "Before Unix timestamp" // @Param after query int false "After Unix timestamp" @@ -723,7 +723,7 @@ func (api *API) workspaceBuildLogs(rw http.ResponseWriter, r *http.Request) { // @ID get-provisioner-state-for-workspace-build // @Security CoderSessionToken // @Produce json -// @Tags Workspaces +// @Tags Builds // @Param workspacebuild path string true "Workspace build ID" // @Success 200 {object} codersdk.WorkspaceBuild // @Router /workspacebuilds/{workspacebuild}/state [get] diff --git a/docs/api/builds.md b/docs/api/builds.md new file mode 100644 index 0000000000000..a2af49243e338 --- /dev/null +++ b/docs/api/builds.md @@ -0,0 +1,585 @@ +# Builds + +> This page is incomplete, stay tuned. + +## Get workspace build + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspacebuilds/{workspacebuild}` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------ | -------- | ------------------ | +| `workspacebuild` | path | string | true | Workspace build ID | + +### Example responses + +> 200 Response + +```json +{ + "build_number": 0, + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "deadline": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", + "initiator_name": "string", + "job": { + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" + }, + "reason": "initiator", + "resources": [ + { + "agents": [ + { + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "architecture": "string", + "connection_timeout_seconds": 0, + "created_at": "2019-08-24T14:15:22Z", + "directory": "string", + "disconnected_at": "2019-08-24T14:15:22Z", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "instance_id": "string", + "last_connected_at": "2019-08-24T14:15:22Z", + "latency": { + "property1": { + "latency_ms": 0, + "preferred": true + }, + "property2": { + "latency_ms": 0, + "preferred": true + } + }, + "name": "string", + "operating_system": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", + "startup_script": "string", + "status": "connecting", + "troubleshooting_url": "string", + "updated_at": "2019-08-24T14:15:22Z", + "version": "string" + } + ], + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "hide": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "metadata": [ + { + "key": "string", + "sensitive": true, + "value": "string" + } + ], + "name": "string", + "type": "string", + "workspace_transition": "start" + } + ], + "status": "pending", + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", + "template_version_name": "string", + "transition": "start", + "updated_at": "2019-08-24T14:15:22Z", + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9", + "workspace_name": "string", + "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", + "workspace_owner_name": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceBuild](schemas.md#codersdkworkspacebuild) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Cancel workspace build + +### Code samples + +```shell +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/cancel \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /workspacebuilds/{workspacebuild}/cancel` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------ | -------- | ------------------ | +| `workspacebuild` | path | string | true | Workspace build ID | + +### Example responses + +> 200 Response + +```json +{ + "detail": "string", + "message": "string", + "validations": [ + { + "detail": "string", + "field": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get workspace build logs + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/logs \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspacebuilds/{workspacebuild}/logs` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ----- | ------- | -------- | --------------------- | +| `workspacebuild` | path | string | true | Workspace build ID | +| `before` | query | integer | false | Before Unix timestamp | +| `after` | query | integer | false | After Unix timestamp | +| `follow` | query | boolean | false | Follow log stream | + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "2019-08-24T14:15:22Z", + "id": 0, + "log_level": "trace", + "log_source": "string", + "output": "string", + "stage": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.ProvisionerJobLog](schemas.md#codersdkprovisionerjoblog) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| -------------- | ------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string | false | | | +| `» id` | integer | false | | | +| `» log_level` | string | false | | | +| `» log_source` | string | false | | | +| `» output` | string | false | | | +| `» stage` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| ----------- | ------- | +| `log_level` | `trace` | +| `log_level` | `debug` | +| `log_level` | `info` | +| `log_level` | `warn` | +| `log_level` | `error` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get workspace resources for workspace build + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/resources \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspacebuilds/{workspacebuild}/resources` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------ | -------- | ------------------ | +| `workspacebuild` | path | string | true | Workspace build ID | + +### Example responses + +> 200 Response + +```json +[ + { + "agents": [ + { + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "architecture": "string", + "connection_timeout_seconds": 0, + "created_at": "2019-08-24T14:15:22Z", + "directory": "string", + "disconnected_at": "2019-08-24T14:15:22Z", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "instance_id": "string", + "last_connected_at": "2019-08-24T14:15:22Z", + "latency": { + "property1": { + "latency_ms": 0, + "preferred": true + }, + "property2": { + "latency_ms": 0, + "preferred": true + } + }, + "name": "string", + "operating_system": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", + "startup_script": "string", + "status": "connecting", + "troubleshooting_url": "string", + "updated_at": "2019-08-24T14:15:22Z", + "version": "string" + } + ], + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "hide": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "metadata": [ + { + "key": "string", + "sensitive": true, + "value": "string" + } + ], + "name": "string", + "type": "string", + "workspace_transition": "start" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.WorkspaceResource](schemas.md#codersdkworkspaceresource) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | ---------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | DisplayName is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on
the client or not. | +| `»»» health` | string | false | | | +| `»»» healthcheck` | `codersdk.Healthcheck` | false | | | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies
an icon to be displayed in the dashboard. | +| `»»» id` | string | false | | | +| `»»» sharing_level` | string | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the
`coder server` or via a hostname-based dev URL. If this is set to true
and there is no app wildcard configured on the server, the app will not
be accessible in the UI. | +| `»»» url` | string | false | | URL is the address being proxied to inside the workspace.
If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» first_connected_at` | string | false | | | +| `»» id` | string | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string | false | | | +| `»» latency` | object | false | | DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | `codersdk.DERPRegion` | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» resource_id` | string | false | | | +| `»» startup_script` | string | false | | | +| `»» status` | string | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string | false | | | +| `»» version` | string | false | | | +| `» created_at` | string | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string | false | | | +| `» job_id` | string | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| ---------------------- | --------------- | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get provisioner state for workspace build + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/state \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspacebuilds/{workspacebuild}/state` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------ | -------- | ------------------ | +| `workspacebuild` | path | string | true | Workspace build ID | + +### Example responses + +> 200 Response + +```json +{ + "build_number": 0, + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "deadline": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", + "initiator_name": "string", + "job": { + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" + }, + "reason": "initiator", + "resources": [ + { + "agents": [ + { + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "architecture": "string", + "connection_timeout_seconds": 0, + "created_at": "2019-08-24T14:15:22Z", + "directory": "string", + "disconnected_at": "2019-08-24T14:15:22Z", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "instance_id": "string", + "last_connected_at": "2019-08-24T14:15:22Z", + "latency": { + "property1": { + "latency_ms": 0, + "preferred": true + }, + "property2": { + "latency_ms": 0, + "preferred": true + } + }, + "name": "string", + "operating_system": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", + "startup_script": "string", + "status": "connecting", + "troubleshooting_url": "string", + "updated_at": "2019-08-24T14:15:22Z", + "version": "string" + } + ], + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "hide": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "metadata": [ + { + "key": "string", + "sensitive": true, + "value": "string" + } + ], + "name": "string", + "type": "string", + "workspace_transition": "start" + } + ], + "status": "pending", + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", + "template_version_name": "string", + "transition": "start", + "updated_at": "2019-08-24T14:15:22Z", + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9", + "workspace_name": "string", + "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", + "workspace_owner_name": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceBuild](schemas.md#codersdkworkspacebuild) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 288465fcab3e5..9254214759b09 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -323,588 +323,6 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. -## Get workspace build - -### Code samples - -```shell -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /workspacebuilds/{workspacebuild}` - -### Parameters - -| Name | In | Type | Required | Description | -| ---------------- | ---- | ------ | -------- | ------------------ | -| `workspacebuild` | path | string | true | Workspace build ID | - -### Example responses - -> 200 Response - -```json -{ - "build_number": 0, - "created_at": "2019-08-24T14:15:22Z", - "daily_cost": 0, - "deadline": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", - "initiator_name": "string", - "job": { - "canceled_at": "2019-08-24T14:15:22Z", - "completed_at": "2019-08-24T14:15:22Z", - "created_at": "2019-08-24T14:15:22Z", - "error": "string", - "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "started_at": "2019-08-24T14:15:22Z", - "status": "pending", - "tags": { - "property1": "string", - "property2": "string" - }, - "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" - }, - "reason": "initiator", - "resources": [ - { - "agents": [ - { - "apps": [ - { - "command": "string", - "display_name": "string", - "external": true, - "health": "string", - "healthcheck": { - "interval": 0, - "threshold": 0, - "url": "string" - }, - "icon": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "sharing_level": "owner", - "slug": "string", - "subdomain": true, - "url": "string" - } - ], - "architecture": "string", - "connection_timeout_seconds": 0, - "created_at": "2019-08-24T14:15:22Z", - "directory": "string", - "disconnected_at": "2019-08-24T14:15:22Z", - "environment_variables": { - "property1": "string", - "property2": "string" - }, - "first_connected_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "instance_id": "string", - "last_connected_at": "2019-08-24T14:15:22Z", - "latency": { - "property1": { - "latency_ms": 0, - "preferred": true - }, - "property2": { - "latency_ms": 0, - "preferred": true - } - }, - "name": "string", - "operating_system": "string", - "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "startup_script": "string", - "status": "connecting", - "troubleshooting_url": "string", - "updated_at": "2019-08-24T14:15:22Z", - "version": "string" - } - ], - "created_at": "2019-08-24T14:15:22Z", - "daily_cost": 0, - "hide": true, - "icon": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", - "metadata": [ - { - "key": "string", - "sensitive": true, - "value": "string" - } - ], - "name": "string", - "type": "string", - "workspace_transition": "start" - } - ], - "status": "pending", - "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", - "template_version_name": "string", - "transition": "start", - "updated_at": "2019-08-24T14:15:22Z", - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9", - "workspace_name": "string", - "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceBuild](schemas.md#codersdkworkspacebuild) | - -To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. - -## Cancel workspace build - -### Code samples - -```shell -# Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/cancel \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`PATCH /workspacebuilds/{workspacebuild}/cancel` - -### Parameters - -| Name | In | Type | Required | Description | -| ---------------- | ---- | ------ | -------- | ------------------ | -| `workspacebuild` | path | string | true | Workspace build ID | - -### Example responses - -> 200 Response - -```json -{ - "detail": "string", - "message": "string", - "validations": [ - { - "detail": "string", - "field": "string" - } - ] -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | - -To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. - -## Get workspace build logs - -### Code samples - -```shell -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/logs \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /workspacebuilds/{workspacebuild}/logs` - -### Parameters - -| Name | In | Type | Required | Description | -| ---------------- | ----- | ------- | -------- | --------------------- | -| `workspacebuild` | path | string | true | Workspace build ID | -| `before` | query | integer | false | Before Unix timestamp | -| `after` | query | integer | false | After Unix timestamp | -| `follow` | query | boolean | false | Follow log stream | - -### Example responses - -> 200 Response - -```json -[ - { - "created_at": "2019-08-24T14:15:22Z", - "id": 0, - "log_level": "trace", - "log_source": "string", - "output": "string", - "stage": "string" - } -] -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.ProvisionerJobLog](schemas.md#codersdkprovisionerjoblog) | - -

Response Schema

- -Status Code **200** - -| Name | Type | Required | Restrictions | Description | -| -------------- | ------- | -------- | ------------ | ----------- | -| `[array item]` | array | false | | | -| `» created_at` | string | false | | | -| `» id` | integer | false | | | -| `» log_level` | string | false | | | -| `» log_source` | string | false | | | -| `» output` | string | false | | | -| `» stage` | string | false | | | - -#### Enumerated Values - -| Property | Value | -| ----------- | ------- | -| `log_level` | `trace` | -| `log_level` | `debug` | -| `log_level` | `info` | -| `log_level` | `warn` | -| `log_level` | `error` | - -To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. - -## Get workspace resources for workspace build - -### Code samples - -```shell -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/resources \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /workspacebuilds/{workspacebuild}/resources` - -### Parameters - -| Name | In | Type | Required | Description | -| ---------------- | ---- | ------ | -------- | ------------------ | -| `workspacebuild` | path | string | true | Workspace build ID | - -### Example responses - -> 200 Response - -```json -[ - { - "agents": [ - { - "apps": [ - { - "command": "string", - "display_name": "string", - "external": true, - "health": "string", - "healthcheck": { - "interval": 0, - "threshold": 0, - "url": "string" - }, - "icon": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "sharing_level": "owner", - "slug": "string", - "subdomain": true, - "url": "string" - } - ], - "architecture": "string", - "connection_timeout_seconds": 0, - "created_at": "2019-08-24T14:15:22Z", - "directory": "string", - "disconnected_at": "2019-08-24T14:15:22Z", - "environment_variables": { - "property1": "string", - "property2": "string" - }, - "first_connected_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "instance_id": "string", - "last_connected_at": "2019-08-24T14:15:22Z", - "latency": { - "property1": { - "latency_ms": 0, - "preferred": true - }, - "property2": { - "latency_ms": 0, - "preferred": true - } - }, - "name": "string", - "operating_system": "string", - "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "startup_script": "string", - "status": "connecting", - "troubleshooting_url": "string", - "updated_at": "2019-08-24T14:15:22Z", - "version": "string" - } - ], - "created_at": "2019-08-24T14:15:22Z", - "daily_cost": 0, - "hide": true, - "icon": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", - "metadata": [ - { - "key": "string", - "sensitive": true, - "value": "string" - } - ], - "name": "string", - "type": "string", - "workspace_transition": "start" - } -] -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.WorkspaceResource](schemas.md#codersdkworkspaceresource) | - -

Response Schema

- -Status Code **200** - -| Name | Type | Required | Restrictions | Description | -| ------------------------------- | ---------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» agents` | array | false | | | -| `»» apps` | array | false | | | -| `»»» command` | string | false | | | -| `»»» display_name` | string | false | | DisplayName is a friendly name for the app. | -| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on
the client or not. | -| `»»» health` | string | false | | | -| `»»» healthcheck` | `codersdk.Healthcheck` | false | | | -| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | -| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies
an icon to be displayed in the dashboard. | -| `»»» id` | string | false | | | -| `»»» sharing_level` | string | false | | | -| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the
`coder server` or via a hostname-based dev URL. If this is set to true
and there is no app wildcard configured on the server, the app will not
be accessible in the UI. | -| `»»» url` | string | false | | URL is the address being proxied to inside the workspace.
If external is specified, this will be opened on the client. | -| `»» architecture` | string | false | | | -| `»» connection_timeout_seconds` | integer | false | | | -| `»» created_at` | string | false | | | -| `»» directory` | string | false | | | -| `»» disconnected_at` | string | false | | | -| `»» environment_variables` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» first_connected_at` | string | false | | | -| `»» id` | string | false | | | -| `»» instance_id` | string | false | | | -| `»» last_connected_at` | string | false | | | -| `»» latency` | object | false | | DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»» [any property]` | `codersdk.DERPRegion` | false | | | -| `»»»» latency_ms` | number | false | | | -| `»»»» preferred` | boolean | false | | | -| `»» name` | string | false | | | -| `»» operating_system` | string | false | | | -| `»» resource_id` | string | false | | | -| `»» startup_script` | string | false | | | -| `»» status` | string | false | | | -| `»» troubleshooting_url` | string | false | | | -| `»» updated_at` | string | false | | | -| `»» version` | string | false | | | -| `» created_at` | string | false | | | -| `» daily_cost` | integer | false | | | -| `» hide` | boolean | false | | | -| `» icon` | string | false | | | -| `» id` | string | false | | | -| `» job_id` | string | false | | | -| `» metadata` | array | false | | | -| `»» key` | string | false | | | -| `»» sensitive` | boolean | false | | | -| `»» value` | string | false | | | -| `» name` | string | false | | | -| `» type` | string | false | | | -| `» workspace_transition` | string | false | | | - -#### Enumerated Values - -| Property | Value | -| ---------------------- | --------------- | -| `sharing_level` | `owner` | -| `sharing_level` | `authenticated` | -| `sharing_level` | `public` | -| `status` | `connecting` | -| `status` | `connected` | -| `status` | `disconnected` | -| `status` | `timeout` | -| `workspace_transition` | `start` | -| `workspace_transition` | `stop` | -| `workspace_transition` | `delete` | - -To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. - -## Get provisioner state for workspace build - -### Code samples - -```shell -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/state \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /workspacebuilds/{workspacebuild}/state` - -### Parameters - -| Name | In | Type | Required | Description | -| ---------------- | ---- | ------ | -------- | ------------------ | -| `workspacebuild` | path | string | true | Workspace build ID | - -### Example responses - -> 200 Response - -```json -{ - "build_number": 0, - "created_at": "2019-08-24T14:15:22Z", - "daily_cost": 0, - "deadline": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", - "initiator_name": "string", - "job": { - "canceled_at": "2019-08-24T14:15:22Z", - "completed_at": "2019-08-24T14:15:22Z", - "created_at": "2019-08-24T14:15:22Z", - "error": "string", - "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "started_at": "2019-08-24T14:15:22Z", - "status": "pending", - "tags": { - "property1": "string", - "property2": "string" - }, - "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" - }, - "reason": "initiator", - "resources": [ - { - "agents": [ - { - "apps": [ - { - "command": "string", - "display_name": "string", - "external": true, - "health": "string", - "healthcheck": { - "interval": 0, - "threshold": 0, - "url": "string" - }, - "icon": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "sharing_level": "owner", - "slug": "string", - "subdomain": true, - "url": "string" - } - ], - "architecture": "string", - "connection_timeout_seconds": 0, - "created_at": "2019-08-24T14:15:22Z", - "directory": "string", - "disconnected_at": "2019-08-24T14:15:22Z", - "environment_variables": { - "property1": "string", - "property2": "string" - }, - "first_connected_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "instance_id": "string", - "last_connected_at": "2019-08-24T14:15:22Z", - "latency": { - "property1": { - "latency_ms": 0, - "preferred": true - }, - "property2": { - "latency_ms": 0, - "preferred": true - } - }, - "name": "string", - "operating_system": "string", - "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "startup_script": "string", - "status": "connecting", - "troubleshooting_url": "string", - "updated_at": "2019-08-24T14:15:22Z", - "version": "string" - } - ], - "created_at": "2019-08-24T14:15:22Z", - "daily_cost": 0, - "hide": true, - "icon": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", - "metadata": [ - { - "key": "string", - "sensitive": true, - "value": "string" - } - ], - "name": "string", - "type": "string", - "workspace_transition": "start" - } - ], - "status": "pending", - "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", - "template_version_name": "string", - "transition": "start", - "updated_at": "2019-08-24T14:15:22Z", - "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9", - "workspace_name": "string", - "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", - "workspace_owner_name": "string" -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceBuild](schemas.md#codersdkworkspacebuild) | - -To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. - ## List workspaces ### Code samples diff --git a/docs/manifest.json b/docs/manifest.json index ee27d96a9c510..079735f2800bb 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -365,6 +365,10 @@ "title": "Agents", "path": "./api/agents.md" }, + { + "title": "Builds", + "path": "./api/builds.md" + }, { "title": "Schemas", "path": "./api/schemas.md" From f37c6b3ce2d77b66334e3f5c37960d56d62fcec4 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 3 Jan 2023 10:26:23 +0100 Subject: [PATCH 09/61] /workspaces --- coderd/apidoc/docs.go | 146 +++++++++++ coderd/apidoc/swagger.json | 131 ++++++++++ coderd/workspacebuilds.go | 25 ++ coderd/workspaceresourceauth.go | 16 +- docs/api/builds.md | 440 ++++++++++++++++++++++++++++++++ docs/api/schemas.md | 41 +++ 6 files changed, 791 insertions(+), 8 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 09b3ccfbfc8fb..0984d5eb2ccef 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1517,6 +1517,112 @@ const docTemplate = `{ } } }, + "/workspaces/{id}/builds": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Builds" + ], + "summary": "Get workspace builds by workspace ID", + "operationId": "get-workspace-builds-by-workspace-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "After ID", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" + }, + { + "type": "string", + "format": "date-time", + "description": "Since timestamp", + "name": "since", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceBuild" + } + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Builds" + ], + "summary": "Create workspace build", + "operationId": "create-workspace-build", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Create workspace build request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateWorkspaceBuildRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceBuild" + } + } + } + } + }, "/workspaces/{workspace}": { "patch": { "security": [ @@ -2104,6 +2210,46 @@ const docTemplate = `{ } } }, + "codersdk.CreateWorkspaceBuildRequest": { + "type": "object", + "required": [ + "transition" + ], + "properties": { + "dry_run": { + "type": "boolean" + }, + "orphan": { + "description": "Orphan may be set for the Destroy transition.", + "type": "boolean" + }, + "parameter_values": { + "description": "ParameterValues are optional. It will write params to the 'workspace' scope.\nThis will overwrite any existing parameters with the same name.\nThis will not delete old params not included in this list.", + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.CreateParameterRequest" + } + }, + "state": { + "type": "array", + "items": { + "type": "integer" + } + }, + "template_version_id": { + "type": "string" + }, + "transition": { + "type": "string", + "enum": [ + "create", + "start", + "stop", + "delete" + ] + } + } + }, "codersdk.DERP": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index e93f620f9260c..0dc03ac3ddd8f 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1320,6 +1320,104 @@ } } }, + "/workspaces/{id}/builds": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Builds"], + "summary": "Get workspace builds by workspace ID", + "operationId": "get-workspace-builds-by-workspace-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "After ID", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" + }, + { + "type": "string", + "format": "date-time", + "description": "Since timestamp", + "name": "since", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceBuild" + } + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Builds"], + "summary": "Create workspace build", + "operationId": "create-workspace-build", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Create workspace build request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateWorkspaceBuildRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceBuild" + } + } + } + } + }, "/workspaces/{workspace}": { "patch": { "security": [ @@ -1852,6 +1950,39 @@ } } }, + "codersdk.CreateWorkspaceBuildRequest": { + "type": "object", + "required": ["transition"], + "properties": { + "dry_run": { + "type": "boolean" + }, + "orphan": { + "description": "Orphan may be set for the Destroy transition.", + "type": "boolean" + }, + "parameter_values": { + "description": "ParameterValues are optional. It will write params to the 'workspace' scope.\nThis will overwrite any existing parameters with the same name.\nThis will not delete old params not included in this list.", + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.CreateParameterRequest" + } + }, + "state": { + "type": "array", + "items": { + "type": "integer" + } + }, + "template_version_id": { + "type": "string" + }, + "transition": { + "type": "string", + "enum": ["create", "start", "stop", "delete"] + } + } + }, "codersdk.DERP": { "type": "object", "properties": { diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index ef8f9b3503ba9..19ea83a06376c 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -72,6 +72,18 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, apiBuild) } +// @Summary Get workspace builds by workspace ID +// @ID get-workspace-builds-by-workspace-id +// @Security CoderSessionToken +// @Produce json +// @Tags Builds +// @Param id path string true "Workspace ID" format(uuid) +// @Param after_id query string false "After ID" format(uuid) +// @Param limit query int false "Page limit" +// @Param offset query int false "Page offset" +// @Param since query string false "Since timestamp" format(date-time) +// @Success 200 {array} codersdk.WorkspaceBuild +// @Router /workspaces/{id}/builds [get] func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspace := httpmw.WorkspaceParam(r) @@ -262,6 +274,19 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ httpapi.Write(ctx, rw, http.StatusOK, apiBuild) } +// @Summary Create workspace build +// @ID create-workspace-build +// @Security CoderSessionToken +// @Accepts json +// @Produce json +// @Tags Builds +// @Param id path string true "Workspace ID" format(uuid) +// @Param request body codersdk.CreateWorkspaceBuildRequest true "Create workspace build request" +// @Success 200 {object} codersdk.WorkspaceBuild +// @Router /workspaces/{id}/builds [post] +// +// Azure supports instance identity verification: +// https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux#tabgroup_14 func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) diff --git a/coderd/workspaceresourceauth.go b/coderd/workspaceresourceauth.go index ea72b794ada37..8da31d1cdd96a 100644 --- a/coderd/workspaceresourceauth.go +++ b/coderd/workspaceresourceauth.go @@ -45,10 +45,6 @@ func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r api.handleAuthInstanceID(rw, r, instanceID) } -// AWS supports instance identity verification: -// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html -// Using this, we can exchange a signed instance payload for an agent token. -// // @Summary Authenticate agent on AWS instance // @ID authenticate-agent-on-aws-instance // @Security CoderSessionToken @@ -57,6 +53,10 @@ func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r // @Param request body codersdk.AWSInstanceIdentityToken true "Instance identity token" // @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse // @Router /workspaceagents/aws-instance-identity [post] +// +// AWS supports instance identity verification: +// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html +// Using this, we can exchange a signed instance payload for an agent token. func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var req codersdk.AWSInstanceIdentityToken @@ -74,10 +74,6 @@ func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r * api.handleAuthInstanceID(rw, r, identity.InstanceID) } -// Google Compute Engine supports instance identity verification: -// https://cloud.google.com/compute/docs/instances/verifying-instance-identity -// Using this, we can exchange a signed instance payload for an agent token. -// // @Summary Authenticate agent on Google Cloud instance // @ID authenticate-agent-on-google-cloud-instance // @Security CoderSessionToken @@ -86,6 +82,10 @@ func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r * // @Param request body codersdk.GoogleInstanceIdentityToken true "Instance identity token" // @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse // @Router /workspaceagents/google-instance-identity [post] +// +// Google Compute Engine supports instance identity verification: +// https://cloud.google.com/compute/docs/instances/verifying-instance-identity +// Using this, we can exchange a signed instance payload for an agent token. func (api *API) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var req codersdk.GoogleInstanceIdentityToken diff --git a/docs/api/builds.md b/docs/api/builds.md index a2af49243e338..2eea39745c4ea 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -583,3 +583,443 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceBuild](schemas.md#codersdkworkspacebuild) | To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get workspace builds by workspace ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspaces/{id}/builds \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspaces/{id}/builds` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------- | ----- | ----------------- | -------- | --------------- | +| `id` | path | string(uuid) | true | Workspace ID | +| `after_id` | query | string(uuid) | false | After ID | +| `limit` | query | integer | false | Page limit | +| `offset` | query | integer | false | Page offset | +| `since` | query | string(date-time) | false | Since timestamp | + +### Example responses + +> 200 Response + +```json +[ + { + "build_number": 0, + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "deadline": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", + "initiator_name": "string", + "job": { + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" + }, + "reason": "initiator", + "resources": [ + { + "agents": [ + { + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "architecture": "string", + "connection_timeout_seconds": 0, + "created_at": "2019-08-24T14:15:22Z", + "directory": "string", + "disconnected_at": "2019-08-24T14:15:22Z", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "instance_id": "string", + "last_connected_at": "2019-08-24T14:15:22Z", + "latency": { + "property1": { + "latency_ms": 0, + "preferred": true + }, + "property2": { + "latency_ms": 0, + "preferred": true + } + }, + "name": "string", + "operating_system": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", + "startup_script": "string", + "status": "connecting", + "troubleshooting_url": "string", + "updated_at": "2019-08-24T14:15:22Z", + "version": "string" + } + ], + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "hide": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "metadata": [ + { + "key": "string", + "sensitive": true, + "value": "string" + } + ], + "name": "string", + "type": "string", + "workspace_transition": "start" + } + ], + "status": "pending", + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", + "template_version_name": "string", + "transition": "start", + "updated_at": "2019-08-24T14:15:22Z", + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9", + "workspace_name": "string", + "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", + "workspace_owner_name": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.WorkspaceBuild](schemas.md#codersdkworkspacebuild) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| -------------------------------- | ------------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» build_number` | integer | false | | | +| `» created_at` | string | false | | | +| `» daily_cost` | integer | false | | | +| `» deadline` | string | false | | | +| `» id` | string | false | | | +| `» initiator_id` | string | false | | | +| `» initiator_name` | string | false | | | +| `» job` | `codersdk.ProvisionerJob` | false | | | +| `»» canceled_at` | string | false | | | +| `»» completed_at` | string | false | | | +| `»» created_at` | string | false | | | +| `»» error` | string | false | | | +| `»» file_id` | string | false | | | +| `»» id` | string | false | | | +| `»» started_at` | string | false | | | +| `»» status` | string | false | | | +| `»» tags` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» worker_id` | string | false | | | +| `» reason` | string | false | | | +| `» resources` | array | false | | | +| `»» agents` | array | false | | | +| `»»» apps` | array | false | | | +| `»»»» command` | string | false | | | +| `»»»» display_name` | string | false | | DisplayName is a friendly name for the app. | +| `»»»» external` | boolean | false | | External specifies whether the URL should be opened externally on
the client or not. | +| `»»»» health` | string | false | | | +| `»»»» healthcheck` | `codersdk.Healthcheck` | false | | | +| `»»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | +| `»»»» icon` | string | false | | Icon is a relative path or external URL that specifies
an icon to be displayed in the dashboard. | +| `»»»» id` | string | false | | | +| `»»»» sharing_level` | string | false | | | +| `»»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the
`coder server` or via a hostname-based dev URL. If this is set to true
and there is no app wildcard configured on the server, the app will not
be accessible in the UI. | +| `»»»» url` | string | false | | URL is the address being proxied to inside the workspace.
If external is specified, this will be opened on the client. | +| `»»» architecture` | string | false | | | +| `»»» connection_timeout_seconds` | integer | false | | | +| `»»» created_at` | string | false | | | +| `»»» directory` | string | false | | | +| `»»» disconnected_at` | string | false | | | +| `»»» environment_variables` | object | false | | | +| `»»»» [any property]` | string | false | | | +| `»»» first_connected_at` | string | false | | | +| `»»» id` | string | false | | | +| `»»» instance_id` | string | false | | | +| `»»» last_connected_at` | string | false | | | +| `»»» latency` | object | false | | DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»»» [any property]` | `codersdk.DERPRegion` | false | | | +| `»»»»» latency_ms` | number | false | | | +| `»»»»» preferred` | boolean | false | | | +| `»»» name` | string | false | | | +| `»»» operating_system` | string | false | | | +| `»»» resource_id` | string | false | | | +| `»»» startup_script` | string | false | | | +| `»»» status` | string | false | | | +| `»»» troubleshooting_url` | string | false | | | +| `»»» updated_at` | string | false | | | +| `»»» version` | string | false | | | +| `»» created_at` | string | false | | | +| `»» daily_cost` | integer | false | | | +| `»» hide` | boolean | false | | | +| `»» icon` | string | false | | | +| `»» id` | string | false | | | +| `»» job_id` | string | false | | | +| `»» metadata` | array | false | | | +| `»»» key` | string | false | | | +| `»»» sensitive` | boolean | false | | | +| `»»» value` | string | false | | | +| `»» name` | string | false | | | +| `»» type` | string | false | | | +| `»» workspace_transition` | string | false | | | +| `» status` | string | false | | | +| `» template_version_id` | string | false | | | +| `» template_version_name` | string | false | | | +| `» transition` | string | false | | | +| `» updated_at` | string | false | | | +| `» workspace_id` | string | false | | | +| `» workspace_name` | string | false | | | +| `» workspace_owner_id` | string | false | | | +| `» workspace_owner_name` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| ---------------------- | --------------- | +| `status` | `pending` | +| `status` | `running` | +| `status` | `succeeded` | +| `status` | `canceling` | +| `status` | `canceled` | +| `status` | `failed` | +| `reason` | `initiator` | +| `reason` | `autostart` | +| `reason` | `autostop` | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | +| `status` | `pending` | +| `status` | `starting` | +| `status` | `running` | +| `status` | `stopping` | +| `status` | `stopped` | +| `status` | `failed` | +| `status` | `canceling` | +| `status` | `canceled` | +| `status` | `deleting` | +| `status` | `deleted` | +| `transition` | `start` | +| `transition` | `stop` | +| `transition` | `delete` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Create workspace build + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/workspaces/{id}/builds \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /workspaces/{id}/builds` + +> Body parameter + +```json +{ + "dry_run": true, + "orphan": true, + "parameter_values": [ + { + "copy_from_parameter": "string", + "destination_scheme": "none", + "name": "string", + "source_scheme": "none", + "source_value": "string" + } + ], + "state": [0], + "template_version_id": "string", + "transition": "create" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | -------------------------------------------------------------------------------------- | -------- | ------------------------------ | +| `id` | path | string(uuid) | true | Workspace ID | +| `body` | body | [codersdk.CreateWorkspaceBuildRequest](schemas.md#codersdkcreateworkspacebuildrequest) | true | Create workspace build request | + +### Example responses + +> 200 Response + +```json +{ + "build_number": 0, + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "deadline": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", + "initiator_name": "string", + "job": { + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" + }, + "reason": "initiator", + "resources": [ + { + "agents": [ + { + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "architecture": "string", + "connection_timeout_seconds": 0, + "created_at": "2019-08-24T14:15:22Z", + "directory": "string", + "disconnected_at": "2019-08-24T14:15:22Z", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "instance_id": "string", + "last_connected_at": "2019-08-24T14:15:22Z", + "latency": { + "property1": { + "latency_ms": 0, + "preferred": true + }, + "property2": { + "latency_ms": 0, + "preferred": true + } + }, + "name": "string", + "operating_system": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", + "startup_script": "string", + "status": "connecting", + "troubleshooting_url": "string", + "updated_at": "2019-08-24T14:15:22Z", + "version": "string" + } + ], + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "hide": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "metadata": [ + { + "key": "string", + "sensitive": true, + "value": "string" + } + ], + "name": "string", + "type": "string", + "workspace_transition": "start" + } + ], + "status": "pending", + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", + "template_version_name": "string", + "transition": "start", + "updated_at": "2019-08-24T14:15:22Z", + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9", + "workspace_name": "string", + "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", + "workspace_owner_name": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceBuild](schemas.md#codersdkworkspacebuild) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 843ed32642484..400a72292f70a 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -513,6 +513,47 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `resource_type` | `api_key` | | `resource_type` | `group` | +## codersdk.CreateWorkspaceBuildRequest + +```json +{ + "dry_run": true, + "orphan": true, + "parameter_values": [ + { + "copy_from_parameter": "string", + "destination_scheme": "none", + "name": "string", + "source_scheme": "none", + "source_value": "string" + } + ], + "state": [0], + "template_version_id": "string", + "transition": "create" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| --------------------- | --------------------------------------------------------------------------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `dry_run` | boolean | false | | | +| `orphan` | boolean | false | | Orphan may be set for the Destroy transition. | +| `parameter_values` | array of [codersdk.CreateParameterRequest](#codersdkcreateparameterrequest) | false | | Parameter values are optional. It will write params to the 'workspace' scope. This will overwrite any existing parameters with the same name. This will not delete old params not included in this list. | +| `state` | array of integer | false | | | +| `template_version_id` | string | false | | | +| `transition` | string | true | | | + +#### Enumerated Values + +| Property | Value | +| ------------ | -------- | +| `transition` | `create` | +| `transition` | `start` | +| `transition` | `stop` | +| `transition` | `delete` | + ## codersdk.DERP ```json From 250982a0899aa846c1a0463f23b1bea0dc5d98b2 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 3 Jan 2023 12:18:39 +0100 Subject: [PATCH 10/61] templates.go, templateversions.go --- coderd/apidoc/docs.go | 254 ++++++++++++++++++++++++- coderd/apidoc/swagger.json | 231 ++++++++++++++++++++++- coderd/templates.go | 8 + coderd/templateversions.go | 27 +++ codersdk/templates.go | 1 + codersdk/templateversions.go | 10 +- codersdk/users.go | 18 +- docs/api/audit.md | 12 +- docs/api/schemas.md | 151 +++++++++++++-- docs/api/templates.md | 353 +++++++++++++++++++++++++++++++++++ 10 files changed, 1015 insertions(+), 50 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 0984d5eb2ccef..01007fab40c5a 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -808,6 +808,169 @@ const docTemplate = `{ } } }, + "/templates/{id}/daus": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get template DAUs by ID", + "operationId": "get-template-daus-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TemplateDAUsResponse" + } + } + } + } + }, + "/templates/{id}/versions": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "List template versions by template ID", + "operationId": "list-template-versions-by-template-ID", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + }, + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Update active template version by template ID", + "operationId": "update-active-template-version-by-template-ID", + "parameters": [ + { + "description": "Modified template version", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateActiveTemplateVersion" + } + }, + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, + "/templates/{id}/versions/{name}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get template version by template ID and name", + "operationId": "get-template-version-by-template-id-and-name", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Template name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + } + }, "/updatecheck": { "get": { "produces": [ @@ -2250,6 +2413,17 @@ const docTemplate = `{ } } }, + "codersdk.DAUEntry": { + "type": "object", + "properties": { + "amount": { + "type": "integer" + }, + "date": { + "type": "string" + } + } + }, "codersdk.DERP": { "type": "object", "properties": { @@ -3100,6 +3274,54 @@ const docTemplate = `{ "$ref": "#/definitions/codersdk.TransitionStats" } }, + "codersdk.TemplateDAUsResponse": { + "type": "object", + "properties": { + "entries": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.DAUEntry" + } + } + } + }, + "codersdk.TemplateVersion": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "created_by": { + "$ref": "#/definitions/codersdk.User" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "job": { + "$ref": "#/definitions/codersdk.ProvisionerJob" + }, + "name": { + "type": "string" + }, + "organization_id": { + "type": "string", + "format": "uuid" + }, + "readme": { + "type": "string" + }, + "template_id": { + "type": "string", + "format": "uuid" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + } + }, "codersdk.TraceConfig": { "type": "object", "properties": { @@ -3127,6 +3349,17 @@ const docTemplate = `{ } } }, + "codersdk.UpdateActiveTemplateVersion": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + }, "codersdk.UpdateCheckResponse": { "type": "object", "properties": { @@ -3186,24 +3419,29 @@ const docTemplate = `{ ], "properties": { "avatar_url": { - "type": "string" + "type": "string", + "format": "uri" }, "created_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "email": { "type": "string" }, "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "last_seen_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "organization_ids": { "type": "array", "items": { - "type": "string" + "type": "string", + "format": "uuid" } }, "roles": { @@ -3213,7 +3451,11 @@ const docTemplate = `{ } }, "status": { - "type": "string" + "type": "string", + "enum": [ + "active", + "suspended" + ] }, "username": { "type": "string" diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 0dc03ac3ddd8f..46c1c2e2ef8b3 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -698,6 +698,151 @@ } } }, + "/templates/{id}/daus": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get template DAUs by ID", + "operationId": "get-template-daus-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TemplateDAUsResponse" + } + } + } + } + }, + "/templates/{id}/versions": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "List template versions by template ID", + "operationId": "list-template-versions-by-template-ID", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + }, + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Update active template version by template ID", + "operationId": "update-active-template-version-by-template-ID", + "parameters": [ + { + "description": "Modified template version", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateActiveTemplateVersion" + } + }, + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, + "/templates/{id}/versions/{name}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get template version by template ID and name", + "operationId": "get-template-version-by-template-id-and-name", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Template name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + } + }, "/updatecheck": { "get": { "produces": ["application/json"], @@ -1983,6 +2128,17 @@ } } }, + "codersdk.DAUEntry": { + "type": "object", + "properties": { + "amount": { + "type": "integer" + }, + "date": { + "type": "string" + } + } + }, "codersdk.DERP": { "type": "object", "properties": { @@ -2812,6 +2968,54 @@ "$ref": "#/definitions/codersdk.TransitionStats" } }, + "codersdk.TemplateDAUsResponse": { + "type": "object", + "properties": { + "entries": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.DAUEntry" + } + } + } + }, + "codersdk.TemplateVersion": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "created_by": { + "$ref": "#/definitions/codersdk.User" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "job": { + "$ref": "#/definitions/codersdk.ProvisionerJob" + }, + "name": { + "type": "string" + }, + "organization_id": { + "type": "string", + "format": "uuid" + }, + "readme": { + "type": "string" + }, + "template_id": { + "type": "string", + "format": "uuid" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + } + }, "codersdk.TraceConfig": { "type": "object", "properties": { @@ -2839,6 +3043,15 @@ } } }, + "codersdk.UpdateActiveTemplateVersion": { + "type": "object", + "required": ["id"], + "properties": { + "id": { + "type": "string" + } + } + }, "codersdk.UpdateCheckResponse": { "type": "object", "properties": { @@ -2893,24 +3106,29 @@ "required": ["created_at", "email", "id", "username"], "properties": { "avatar_url": { - "type": "string" + "type": "string", + "format": "uri" }, "created_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "email": { "type": "string" }, "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "last_seen_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "organization_ids": { "type": "array", "items": { - "type": "string" + "type": "string", + "format": "uuid" } }, "roles": { @@ -2920,7 +3138,8 @@ } }, "status": { - "type": "string" + "type": "string", + "enum": ["active", "suspended"] }, "username": { "type": "string" diff --git a/coderd/templates.go b/coderd/templates.go index 662a3069683c7..a7680355ee05d 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -599,6 +599,14 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, api.convertTemplate(updated, count, createdByNameMap[updated.ID.String()])) } +// @Summary Get template DAUs by ID +// @ID get-template-daus-by-id +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param id path string true "Template ID" format(uuid) +// @Success 200 {object} codersdk.TemplateDAUsResponse +// @Router /templates/{id}/daus [get] func (api *API) templateDAUs(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() template := httpmw.TemplateParam(r) diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 070500a26d8c9..94285d5364499 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -454,6 +454,14 @@ func (api *API) fetchTemplateVersionDryRunJob(rw http.ResponseWriter, r *http.Re return job, true } +// @Summary List template versions by template ID +// @ID list-template-versions-by-template-ID +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param id path string true "Template ID" format(uuid) +// @Success 200 {array} codersdk.TemplateVersion +// @Router /templates/{id}/versions [get] func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() template := httpmw.TemplateParam(r) @@ -551,6 +559,15 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque httpapi.Write(ctx, rw, http.StatusOK, apiVersions) } +// @Summary Get template version by template ID and name +// @ID get-template-version-by-template-id-and-name +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param id path string true "Template ID" format(uuid) +// @Param name path string true "Template name" +// @Success 200 {array} codersdk.TemplateVersion +// @Router /templates/{id}/versions/{name} [get] func (api *API) templateVersionByName(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() template := httpmw.TemplateParam(r) @@ -708,6 +725,16 @@ func (api *API) previousTemplateVersionByOrganizationAndName(rw http.ResponseWri httpapi.Write(ctx, rw, http.StatusOK, convertTemplateVersion(previousTemplateVersion, convertProvisionerJob(job), user)) } +// @Summary Update active template version by template ID +// @ID update-active-template-version-by-template-ID +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Templates +// @Param request body codersdk.UpdateActiveTemplateVersion true "Modified template version" +// @Param id path string true "Template ID" format(uuid) +// @Success 200 {object} codersdk.Response +// @Router /templates/{id}/versions [patch] func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/codersdk/templates.go b/codersdk/templates.go index 3e066beb8bf7f..ee14775430005 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -214,6 +214,7 @@ type DAUEntry struct { Amount int `json:"amount"` } +// TemplateDAUsResponse contains statistics of daily active users of the template. type TemplateDAUsResponse struct { Entries []DAUEntry `json:"entries"` } diff --git a/codersdk/templateversions.go b/codersdk/templateversions.go index b9ea141f50654..1be7881ba5ae2 100644 --- a/codersdk/templateversions.go +++ b/codersdk/templateversions.go @@ -13,11 +13,11 @@ import ( // TemplateVersion represents a single version of a template. type TemplateVersion struct { - ID uuid.UUID `json:"id"` - TemplateID *uuid.UUID `json:"template_id,omitempty"` - OrganizationID uuid.UUID `json:"organization_id,omitempty"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID uuid.UUID `json:"id" format:"uuid"` + TemplateID *uuid.UUID `json:"template_id,omitempty" format:"uuid"` + OrganizationID uuid.UUID `json:"organization_id,omitempty" format:"uuid"` + CreatedAt time.Time `json:"created_at" format:"date-time"` + UpdatedAt time.Time `json:"updated_at" format:"date-time"` Name string `json:"name"` Job ProvisionerJob `json:"job"` Readme string `json:"readme"` diff --git a/codersdk/users.go b/codersdk/users.go index 6561c506fee0b..4b1f24afac933 100644 --- a/codersdk/users.go +++ b/codersdk/users.go @@ -35,16 +35,16 @@ type UsersRequest struct { // User represents a user in Coder. type User struct { - ID uuid.UUID `json:"id" validate:"required" table:"id"` + ID uuid.UUID `json:"id" validate:"required" table:"id" format:"uuid"` Username string `json:"username" validate:"required" table:"username"` Email string `json:"email" validate:"required" table:"email"` - CreatedAt time.Time `json:"created_at" validate:"required" table:"created at"` - LastSeenAt time.Time `json:"last_seen_at"` + CreatedAt time.Time `json:"created_at" validate:"required" table:"created at" format:"date-time"` + LastSeenAt time.Time `json:"last_seen_at" format:"date-time"` - Status UserStatus `json:"status" table:"status"` - OrganizationIDs []uuid.UUID `json:"organization_ids"` + Status UserStatus `json:"status" table:"status" enums:"active,suspended"` + OrganizationIDs []uuid.UUID `json:"organization_ids" format:"uuid"` Roles []Role `json:"roles"` - AvatarURL string `json:"avatar_url"` + AvatarURL string `json:"avatar_url" format:"uri"` } type GetUsersResponse struct { @@ -61,15 +61,15 @@ type CreateFirstUserRequest struct { // CreateFirstUserResponse contains IDs for newly created user info. type CreateFirstUserResponse struct { - UserID uuid.UUID `json:"user_id"` - OrganizationID uuid.UUID `json:"organization_id"` + UserID uuid.UUID `json:"user_id" format:"uuid"` + OrganizationID uuid.UUID `json:"organization_id" format:"uuid"` } type CreateUserRequest struct { Email string `json:"email" validate:"required,email"` Username string `json:"username" validate:"required,username"` Password string `json:"password" validate:"required"` - OrganizationID uuid.UUID `json:"organization_id" validate:"required"` + OrganizationID uuid.UUID `json:"organization_id" validate:"required" format:"uuid"` } type UpdateUserProfileRequest struct { diff --git a/docs/api/audit.md b/docs/api/audit.md index 8b3946c715665..d92f7233c9208 100644 --- a/docs/api/audit.md +++ b/docs/api/audit.md @@ -60,19 +60,19 @@ curl -X GET http://coder-server:8080/api/v2/audit?q=string \ "status_code": 0, "time": "string", "user": { - "avatar_url": "string", - "created_at": "string", + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", "email": "string", - "id": "string", - "last_seen_at": "string", - "organization_ids": ["string"], + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "roles": [ { "display_name": "string", "name": "string" } ], - "status": "string", + "status": "active", "username": "string" }, "user_agent": "string" diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 400a72292f70a..f9197ba8f1583 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -163,19 +163,19 @@ "status_code": 0, "time": "string", "user": { - "avatar_url": "string", - "created_at": "string", + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", "email": "string", - "id": "string", - "last_seen_at": "string", - "organization_ids": ["string"], + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "roles": [ { "display_name": "string", "name": "string" } ], - "status": "string", + "status": "active", "username": "string" }, "user_agent": "string" @@ -239,19 +239,19 @@ "status_code": 0, "time": "string", "user": { - "avatar_url": "string", - "created_at": "string", + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", "email": "string", - "id": "string", - "last_seen_at": "string", - "organization_ids": ["string"], + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "roles": [ { "display_name": "string", "name": "string" } ], - "status": "string", + "status": "active", "username": "string" }, "user_agent": "string" @@ -554,6 +554,22 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `transition` | `stop` | | `transition` | `delete` | +## codersdk.DAUEntry + +```json +{ + "amount": 0, + "date": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------- | ------- | -------- | ------------ | ----------- | +| `amount` | integer | false | | | +| `date` | string | false | | | + ## codersdk.DERP ```json @@ -2854,6 +2870,84 @@ Parameter represents a set value for the scope. | ---------------- | ---------------------------------------------------- | -------- | ------------ | ----------- | | `[any property]` | [codersdk.TransitionStats](#codersdktransitionstats) | false | | | +## codersdk.TemplateDAUsResponse + +```json +{ + "entries": [ + { + "amount": 0, + "date": "string" + } + ] +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| --------- | ----------------------------------------------- | -------- | ------------ | ----------- | +| `entries` | array of [codersdk.DAUEntry](#codersdkdauentry) | false | | | + +## codersdk.TemplateVersion + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "created_by": { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + }, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job": { + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" + }, + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "readme": "string", + "template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc", + "updated_at": "2019-08-24T14:15:22Z" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------------- | -------------------------------------------------- | -------- | ------------ | ----------- | +| `created_at` | string | false | | | +| `created_by` | [codersdk.User](#codersdkuser) | false | | | +| `id` | string | false | | | +| `job` | [codersdk.ProvisionerJob](#codersdkprovisionerjob) | false | | | +| `name` | string | false | | | +| `organization_id` | string | false | | | +| `readme` | string | false | | | +| `template_id` | string | false | | | +| `updated_at` | string | false | | | + ## codersdk.TraceConfig ```json @@ -2918,6 +3012,20 @@ Parameter represents a set value for the scope. | `p50` | integer | false | | | | `p95` | integer | false | | | +## codersdk.UpdateActiveTemplateVersion + +```json +{ + "id": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---- | ------ | -------- | ------------ | ----------- | +| `id` | string | true | | | + ## codersdk.UpdateCheckResponse ```json @@ -2996,19 +3104,19 @@ Parameter represents a set value for the scope. ```json { - "avatar_url": "string", - "created_at": "string", + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", "email": "string", - "id": "string", - "last_seen_at": "string", - "organization_ids": ["string"], + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "roles": [ { "display_name": "string", "name": "string" } ], - "status": "string", + "status": "active", "username": "string" } ``` @@ -3027,6 +3135,13 @@ Parameter represents a set value for the scope. | `status` | string | false | | | | `username` | string | true | | | +#### Enumerated Values + +| Property | Value | +| -------- | ----------- | +| `status` | `active` | +| `status` | `suspended` | + ## codersdk.ValidationError ```json diff --git a/docs/api/templates.md b/docs/api/templates.md index 0cde6eaef5926..997383746bfb9 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -411,3 +411,356 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{id} \ | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Template](schemas.md#codersdktemplate) | To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get template DAUs by ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templates/{id}/daus \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templates/{id}/daus` + +### Parameters + +| Name | In | Type | Required | Description | +| ---- | ---- | ------------ | -------- | ----------- | +| `id` | path | string(uuid) | true | Template ID | + +### Example responses + +> 200 Response + +```json +{ + "entries": [ + { + "amount": 0, + "date": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.TemplateDAUsResponse](schemas.md#codersdktemplatedausresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## List template versions by template ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templates/{id}/versions \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templates/{id}/versions` + +### Parameters + +| Name | In | Type | Required | Description | +| ---- | ---- | ------------ | -------- | ----------- | +| `id` | path | string(uuid) | true | Template ID | + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "2019-08-24T14:15:22Z", + "created_by": { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + }, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job": { + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" + }, + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "readme": "string", + "template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc", + "updated_at": "2019-08-24T14:15:22Z" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.TemplateVersion](schemas.md#codersdktemplateversion) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| --------------------- | ------------------------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string | false | | | +| `» created_by` | `codersdk.User` | false | | | +| `»» avatar_url` | string | false | | | +| `»» created_at` | string | true | | | +| `»» email` | string | true | | | +| `»» id` | string | true | | | +| `»» last_seen_at` | string | false | | | +| `»» organization_ids` | array | false | | | +| `»» roles` | array | false | | | +| `»»» display_name` | string | false | | | +| `»»» name` | string | false | | | +| `»» status` | string | false | | | +| `»» username` | string | true | | | +| `» id` | string | false | | | +| `» job` | `codersdk.ProvisionerJob` | false | | | +| `»» canceled_at` | string | false | | | +| `»» completed_at` | string | false | | | +| `»» created_at` | string | false | | | +| `»» error` | string | false | | | +| `»» file_id` | string | false | | | +| `»» id` | string | false | | | +| `»» started_at` | string | false | | | +| `»» status` | string | false | | | +| `»» tags` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» worker_id` | string | false | | | +| `» name` | string | false | | | +| `» organization_id` | string | false | | | +| `» readme` | string | false | | | +| `» template_id` | string | false | | | +| `» updated_at` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| -------- | ----------- | +| `status` | `active` | +| `status` | `suspended` | +| `status` | `pending` | +| `status` | `running` | +| `status` | `succeeded` | +| `status` | `canceling` | +| `status` | `canceled` | +| `status` | `failed` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Update active template version by template ID + +### Code samples + +```shell +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/templates/{id}/versions \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /templates/{id}/versions` + +> Body parameter + +```json +{ + "id": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | -------------------------------------------------------------------------------------- | -------- | ------------------------- | +| `id` | path | string(uuid) | true | Template ID | +| `body` | body | [codersdk.UpdateActiveTemplateVersion](schemas.md#codersdkupdateactivetemplateversion) | true | Modified template version | + +### Example responses + +> 200 Response + +```json +{ + "detail": "string", + "message": "string", + "validations": [ + { + "detail": "string", + "field": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get template version by template ID and name + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templates/{id}/versions/{name} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templates/{id}/versions/{name}` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------------ | -------- | ------------- | +| `id` | path | string(uuid) | true | Template ID | +| `name` | path | string | true | Template name | + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "2019-08-24T14:15:22Z", + "created_by": { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + }, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job": { + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" + }, + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "readme": "string", + "template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc", + "updated_at": "2019-08-24T14:15:22Z" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.TemplateVersion](schemas.md#codersdktemplateversion) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| --------------------- | ------------------------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string | false | | | +| `» created_by` | `codersdk.User` | false | | | +| `»» avatar_url` | string | false | | | +| `»» created_at` | string | true | | | +| `»» email` | string | true | | | +| `»» id` | string | true | | | +| `»» last_seen_at` | string | false | | | +| `»» organization_ids` | array | false | | | +| `»» roles` | array | false | | | +| `»»» display_name` | string | false | | | +| `»»» name` | string | false | | | +| `»» status` | string | false | | | +| `»» username` | string | true | | | +| `» id` | string | false | | | +| `» job` | `codersdk.ProvisionerJob` | false | | | +| `»» canceled_at` | string | false | | | +| `»» completed_at` | string | false | | | +| `»» created_at` | string | false | | | +| `»» error` | string | false | | | +| `»» file_id` | string | false | | | +| `»» id` | string | false | | | +| `»» started_at` | string | false | | | +| `»» status` | string | false | | | +| `»» tags` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» worker_id` | string | false | | | +| `» name` | string | false | | | +| `» organization_id` | string | false | | | +| `» readme` | string | false | | | +| `» template_id` | string | false | | | +| `» updated_at` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| -------- | ----------- | +| `status` | `active` | +| `status` | `suspended` | +| `status` | `pending` | +| `status` | `running` | +| `status` | `succeeded` | +| `status` | `canceling` | +| `status` | `canceled` | +| `status` | `failed` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. From ff622a7ba085200e45feee07c69dc670392a7083 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 3 Jan 2023 12:39:05 +0100 Subject: [PATCH 11/61] templateversion.go in progress --- coderd/apidoc/docs.go | 148 ++++++++++++++++++++++++++++++++- coderd/apidoc/swagger.json | 133 ++++++++++++++++++++++++++++- coderd/templateversions.go | 16 ++++ codersdk/parameters.go | 14 ++-- docs/api/builds.md | 2 +- docs/api/parameters.md | 2 +- docs/api/schemas.md | 62 +++++++++++++- docs/api/templates.md | 166 ++++++++++++++++++++++++++++++++++++- 8 files changed, 528 insertions(+), 15 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 01007fab40c5a..2cb3cd9fd2125 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -971,6 +971,79 @@ const docTemplate = `{ } } }, + "/templateversions/{id}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get template version by ID", + "operationId": "get-template-version-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + }, + "/templateversions/{id}/schema": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get template version schema by template version ID", + "operationId": "get-template-version-schema-by-template-version-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ParameterSchema" + } + } + } + } + } + }, "/updatecheck": { "get": { "produces": [ @@ -2270,7 +2343,8 @@ const docTemplate = `{ "properties": { "copy_from_parameter": { "description": "CloneID allows copying the value of another parameter.\nThe other param must be related to the same template_id for this to\nsucceed.\nNo other fields are required if using this, as all fields will be copied\nfrom the other parameter.", - "type": "string" + "type": "string", + "format": "uuid" }, "destination_scheme": { "type": "string", @@ -2964,6 +3038,78 @@ const docTemplate = `{ } } }, + "codersdk.ParameterSchema": { + "type": "object", + "properties": { + "allow_override_destination": { + "type": "boolean" + }, + "allow_override_source": { + "type": "boolean" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "default_destination_scheme": { + "type": "string", + "enum": [ + "none", + "environment_variable", + "provisioner_variable" + ] + }, + "default_refresh": { + "type": "string" + }, + "default_source_scheme": { + "type": "string", + "enum": [ + "none", + "data" + ] + }, + "default_source_value": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "job_id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "redisplay_value": { + "type": "boolean" + }, + "validation_condition": { + "type": "string" + }, + "validation_contains": { + "description": "This is a special array of items provided if the validation condition\nexplicitly states the value must be one of a set.", + "type": "array", + "items": { + "type": "string" + } + }, + "validation_error": { + "type": "string" + }, + "validation_type_system": { + "type": "string" + }, + "validation_value_type": { + "type": "string" + } + } + }, "codersdk.PostWorkspaceAgentVersionRequest": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 46c1c2e2ef8b3..991e8b8b69a16 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -843,6 +843,71 @@ } } }, + "/templateversions/{id}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get template version by ID", + "operationId": "get-template-version-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + }, + "/templateversions/{id}/schema": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get template version schema by template version ID", + "operationId": "get-template-version-schema-by-template-version-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ParameterSchema" + } + } + } + } + } + }, "/updatecheck": { "get": { "produces": ["application/json"], @@ -2008,7 +2073,8 @@ "properties": { "copy_from_parameter": { "description": "CloneID allows copying the value of another parameter.\nThe other param must be related to the same template_id for this to\nsucceed.\nNo other fields are required if using this, as all fields will be copied\nfrom the other parameter.", - "type": "string" + "type": "string", + "format": "uuid" }, "destination_scheme": { "type": "string", @@ -2666,6 +2732,71 @@ } } }, + "codersdk.ParameterSchema": { + "type": "object", + "properties": { + "allow_override_destination": { + "type": "boolean" + }, + "allow_override_source": { + "type": "boolean" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "default_destination_scheme": { + "type": "string", + "enum": ["none", "environment_variable", "provisioner_variable"] + }, + "default_refresh": { + "type": "string" + }, + "default_source_scheme": { + "type": "string", + "enum": ["none", "data"] + }, + "default_source_value": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "job_id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "redisplay_value": { + "type": "boolean" + }, + "validation_condition": { + "type": "string" + }, + "validation_contains": { + "description": "This is a special array of items provided if the validation condition\nexplicitly states the value must be one of a set.", + "type": "array", + "items": { + "type": "string" + } + }, + "validation_error": { + "type": "string" + }, + "validation_type_system": { + "type": "string" + }, + "validation_value_type": { + "type": "string" + } + } + }, "codersdk.PostWorkspaceAgentVersionRequest": { "type": "object", "properties": { diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 94285d5364499..9e43040526895 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -28,6 +28,14 @@ import ( "github.com/coder/coder/examples" ) +// @Summary Get template version by ID +// @ID get-template-version-by-id +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param id path string true "Template version ID" format(uuid) +// @Success 200 {object} codersdk.TemplateVersion +// @Router /templateversions/{id} [get] func (api *API) templateVersion(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var ( @@ -116,6 +124,14 @@ func (api *API) patchCancelTemplateVersion(rw http.ResponseWriter, r *http.Reque }) } +// @Summary Get template version schema by template version ID +// @ID get-template-version-schema-by-template-version-id +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param id path string true "Template version ID" format(uuid) +// @Success 200 {array} codersdk.ParameterSchema +// @Router /templateversions/{id}/schema [get] func (api *API) templateVersionSchema(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var ( diff --git a/codersdk/parameters.go b/codersdk/parameters.go index 92717e55e1651..086e88817a415 100644 --- a/codersdk/parameters.go +++ b/codersdk/parameters.go @@ -44,7 +44,7 @@ const ( type ComputedParameter struct { Parameter SourceValue string `json:"source_value"` - SchemaID uuid.UUID `json:"schema_id"` + SchemaID uuid.UUID `json:"schema_id" format:"uuid"` DefaultSourceValue bool `json:"default_source_value"` } @@ -63,15 +63,15 @@ type Parameter struct { } type ParameterSchema struct { - ID uuid.UUID `json:"id"` - CreatedAt time.Time `json:"created_at"` - JobID uuid.UUID `json:"job_id"` + ID uuid.UUID `json:"id" format:"uuid"` + CreatedAt time.Time `json:"created_at" format:"date-time"` + JobID uuid.UUID `json:"job_id" format:"uuid"` Name string `json:"name"` Description string `json:"description"` - DefaultSourceScheme ParameterSourceScheme `json:"default_source_scheme"` + DefaultSourceScheme ParameterSourceScheme `json:"default_source_scheme" enums:"none,data"` DefaultSourceValue string `json:"default_source_value"` AllowOverrideSource bool `json:"allow_override_source"` - DefaultDestinationScheme ParameterDestinationScheme `json:"default_destination_scheme"` + DefaultDestinationScheme ParameterDestinationScheme `json:"default_destination_scheme" enums:"none,environment_variable,provisioner_variable"` AllowOverrideDestination bool `json:"allow_override_destination"` DefaultRefresh string `json:"default_refresh"` RedisplayValue bool `json:"redisplay_value"` @@ -94,7 +94,7 @@ type CreateParameterRequest struct { // succeed. // No other fields are required if using this, as all fields will be copied // from the other parameter. - CloneID uuid.UUID `json:"copy_from_parameter,omitempty" validate:""` + CloneID uuid.UUID `json:"copy_from_parameter,omitempty" validate:"" format:"uuid"` Name string `json:"name" validate:"required"` SourceValue string `json:"source_value" validate:"required"` diff --git a/docs/api/builds.md b/docs/api/builds.md index 2eea39745c4ea..25e777566de55 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -881,7 +881,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{id}/builds \ "orphan": true, "parameter_values": [ { - "copy_from_parameter": "string", + "copy_from_parameter": "000e07d6-021d-446c-be14-48a9c20bca0b", "destination_scheme": "none", "name": "string", "source_scheme": "none", diff --git a/docs/api/parameters.md b/docs/api/parameters.md index 5b035413f09b0..5b0ca38fc1d30 100644 --- a/docs/api/parameters.md +++ b/docs/api/parameters.md @@ -104,7 +104,7 @@ curl -X POST http://coder-server:8080/api/v2/parameters/{scope}/{id} \ ```json { - "copy_from_parameter": "string", + "copy_from_parameter": "000e07d6-021d-446c-be14-48a9c20bca0b", "destination_scheme": "none", "name": "string", "source_scheme": "none", diff --git a/docs/api/schemas.md b/docs/api/schemas.md index f9197ba8f1583..fd3966a20ac5d 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -407,7 +407,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ```json { - "copy_from_parameter": "string", + "copy_from_parameter": "000e07d6-021d-446c-be14-48a9c20bca0b", "destination_scheme": "none", "name": "string", "source_scheme": "none", @@ -449,7 +449,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a "name": "string", "parameter_values": [ { - "copy_from_parameter": "string", + "copy_from_parameter": "000e07d6-021d-446c-be14-48a9c20bca0b", "destination_scheme": "none", "name": "string", "source_scheme": "none", @@ -521,7 +521,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a "orphan": true, "parameter_values": [ { - "copy_from_parameter": "string", + "copy_from_parameter": "000e07d6-021d-446c-be14-48a9c20bca0b", "destination_scheme": "none", "name": "string", "source_scheme": "none", @@ -2292,6 +2292,62 @@ Parameter represents a set value for the scope. | `source_scheme` | `none` | | `source_scheme` | `data` | +## codersdk.ParameterSchema + +```json +{ + "allow_override_destination": true, + "allow_override_source": true, + "created_at": "2019-08-24T14:15:22Z", + "default_destination_scheme": "none", + "default_refresh": "string", + "default_source_scheme": "none", + "default_source_value": "string", + "description": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "name": "string", + "redisplay_value": true, + "validation_condition": "string", + "validation_contains": ["string"], + "validation_error": "string", + "validation_type_system": "string", + "validation_value_type": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------------------------- | --------------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------- | +| `allow_override_destination` | boolean | false | | | +| `allow_override_source` | boolean | false | | | +| `created_at` | string | false | | | +| `default_destination_scheme` | string | false | | | +| `default_refresh` | string | false | | | +| `default_source_scheme` | string | false | | | +| `default_source_value` | string | false | | | +| `description` | string | false | | | +| `id` | string | false | | | +| `job_id` | string | false | | | +| `name` | string | false | | | +| `redisplay_value` | boolean | false | | | +| `validation_condition` | string | false | | | +| `validation_contains` | array of string | false | | This is a special array of items provided if the validation condition explicitly states the value must be one of a set. | +| `validation_error` | string | false | | | +| `validation_type_system` | string | false | | | +| `validation_value_type` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| ---------------------------- | ---------------------- | +| `default_destination_scheme` | `none` | +| `default_destination_scheme` | `environment_variable` | +| `default_destination_scheme` | `provisioner_variable` | +| `default_source_scheme` | `none` | +| `default_source_scheme` | `data` | + ## codersdk.PostWorkspaceAgentVersionRequest ```json diff --git a/docs/api/templates.md b/docs/api/templates.md index 997383746bfb9..d17f0e4ea0f1a 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -28,7 +28,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization-id}/tem "name": "string", "parameter_values": [ { - "copy_from_parameter": "string", + "copy_from_parameter": "000e07d6-021d-446c-be14-48a9c20bca0b", "destination_scheme": "none", "name": "string", "source_scheme": "none", @@ -764,3 +764,167 @@ Status Code **200** | `status` | `failed` | To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get template version by ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templateversions/{id} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templateversions/{id}` + +### Parameters + +| Name | In | Type | Required | Description | +| ---- | ---- | ------------ | -------- | ------------------- | +| `id` | path | string(uuid) | true | Template version ID | + +### Example responses + +> 200 Response + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "created_by": { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + }, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job": { + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" + }, + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "readme": "string", + "template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc", + "updated_at": "2019-08-24T14:15:22Z" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.TemplateVersion](schemas.md#codersdktemplateversion) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get template version schema by template version ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templateversions/{id}/schema \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templateversions/{id}/schema` + +### Parameters + +| Name | In | Type | Required | Description | +| ---- | ---- | ------------ | -------- | ------------------- | +| `id` | path | string(uuid) | true | Template version ID | + +### Example responses + +> 200 Response + +```json +[ + { + "allow_override_destination": true, + "allow_override_source": true, + "created_at": "2019-08-24T14:15:22Z", + "default_destination_scheme": "none", + "default_refresh": "string", + "default_source_scheme": "none", + "default_source_value": "string", + "description": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "name": "string", + "redisplay_value": true, + "validation_condition": "string", + "validation_contains": ["string"], + "validation_error": "string", + "validation_type_system": "string", + "validation_value_type": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.ParameterSchema](schemas.md#codersdkparameterschema) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| ------------------------------ | ------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» allow_override_destination` | boolean | false | | | +| `» allow_override_source` | boolean | false | | | +| `» created_at` | string | false | | | +| `» default_destination_scheme` | string | false | | | +| `» default_refresh` | string | false | | | +| `» default_source_scheme` | string | false | | | +| `» default_source_value` | string | false | | | +| `» description` | string | false | | | +| `» id` | string | false | | | +| `» job_id` | string | false | | | +| `» name` | string | false | | | +| `» redisplay_value` | boolean | false | | | +| `» validation_condition` | string | false | | | +| `» validation_contains` | array | false | | This is a special array of items provided if the validation condition
explicitly states the value must be one of a set. | +| `» validation_error` | string | false | | | +| `» validation_type_system` | string | false | | | +| `» validation_value_type` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| ---------------------------- | ---------------------- | +| `default_destination_scheme` | `none` | +| `default_destination_scheme` | `environment_variable` | +| `default_destination_scheme` | `provisioner_variable` | +| `default_source_scheme` | `none` | +| `default_source_scheme` | `data` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. From 9dd385cfa4d4ced395e306de35685dc5e8e2862f Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 3 Jan 2023 13:05:24 +0100 Subject: [PATCH 12/61] cancel --- coderd/apidoc/docs.go | 79 ++++++++++++++++++++++- coderd/apidoc/swagger.json | 75 +++++++++++++++++++++- coderd/coderd.go | 1 - coderd/templateversions.go | 16 +++++ docs/api/schemas.md | 34 ++++++++++ docs/api/templates.md | 124 ++++++++++++++++++++++--------------- 6 files changed, 271 insertions(+), 58 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 2cb3cd9fd2125..34b0996f10a74 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1006,6 +1006,41 @@ const docTemplate = `{ } } }, + "/templateversions/{id}/cancel": { + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Cancel template version by ID", + "operationId": "cancel-template-version-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, "/templateversions/{id}/schema": { "get": { "security": [ @@ -1019,8 +1054,8 @@ const docTemplate = `{ "tags": [ "Templates" ], - "summary": "Get template version schema by template version ID", - "operationId": "get-template-version-schema-by-template-version-id", + "summary": "Get template version schema by ID", + "operationId": "get-template-version-schema-by-id", "parameters": [ { "type": "string", @@ -1037,7 +1072,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.ParameterSchema" + "$ref": "#/definitions/parameter.ComputedValue" } } } @@ -4057,6 +4092,44 @@ const docTemplate = `{ "netip.Addr": { "type": "object" }, + "parameter.ComputedValue": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "default_source_value": { + "type": "boolean" + }, + "destination_scheme": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "schema_id": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "scope_id": { + "type": "string" + }, + "source_scheme": { + "type": "string" + }, + "source_value": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, "tailcfg.DERPMap": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 991e8b8b69a16..ec759a22890dc 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -874,6 +874,37 @@ } } }, + "/templateversions/{id}/cancel": { + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Cancel template version by ID", + "operationId": "cancel-template-version-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, "/templateversions/{id}/schema": { "get": { "security": [ @@ -883,8 +914,8 @@ ], "produces": ["application/json"], "tags": ["Templates"], - "summary": "Get template version schema by template version ID", - "operationId": "get-template-version-schema-by-template-version-id", + "summary": "Get template version schema by ID", + "operationId": "get-template-version-schema-by-id", "parameters": [ { "type": "string", @@ -901,7 +932,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.ParameterSchema" + "$ref": "#/definitions/parameter.ComputedValue" } } } @@ -3702,6 +3733,44 @@ "netip.Addr": { "type": "object" }, + "parameter.ComputedValue": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "default_source_value": { + "type": "boolean" + }, + "destination_scheme": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "schema_id": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "scope_id": { + "type": "string" + }, + "source_scheme": { + "type": "string" + }, + "source_value": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, "tailcfg.DERPMap": { "type": "object", "properties": { diff --git a/coderd/coderd.go b/coderd/coderd.go index f66bd82aa630b..51ccdced50442 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -409,7 +409,6 @@ func New(options *Options) *API { apiKeyMiddleware, httpmw.ExtractTemplateVersionParam(options.Database), ) - r.Get("/", api.templateVersion) r.Patch("/cancel", api.patchCancelTemplateVersion) r.Get("/schema", api.templateVersionSchema) diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 9e43040526895..594d86e9c8888 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -69,6 +69,14 @@ func (api *API) templateVersion(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, convertTemplateVersion(templateVersion, convertProvisionerJob(job), user)) } +// @Summary Cancel template version by ID +// @ID cancel-template-version-by-id +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param id path string true "Template version ID" format(uuid) +// @Success 200 {object} codersdk.Response +// @Router /templateversions/{id}/cancel [patch] func (api *API) patchCancelTemplateVersion(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var ( @@ -184,6 +192,14 @@ func (api *API) templateVersionSchema(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, apiSchemas) } +// @Summary Get template version schema by ID +// @ID get-template-version-schema-by-id +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param id path string true "Template version ID" format(uuid) +// @Success 200 {array} parameter.ComputedValue +// @Router /templateversions/{id}/schema [get] func (api *API) templateVersionParameters(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var ( diff --git a/docs/api/schemas.md b/docs/api/schemas.md index fd3966a20ac5d..ab8bfa6a5fb3a 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -4061,6 +4061,40 @@ Parameter represents a set value for the scope. _None_ +## parameter.ComputedValue + +```json +{ + "created_at": "string", + "default_source_value": true, + "destination_scheme": "string", + "id": "string", + "name": "string", + "schema_id": "string", + "scope": "string", + "scope_id": "string", + "source_scheme": "string", + "source_value": "string", + "updated_at": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------------------- | ------- | -------- | ------------ | ----------- | +| `created_at` | string | false | | | +| `default_source_value` | boolean | false | | | +| `destination_scheme` | string | false | | | +| `id` | string | false | | | +| `name` | string | false | | | +| `schema_id` | string | false | | | +| `scope` | string | false | | | +| `scope_id` | string | false | | | +| `source_scheme` | string | false | | | +| `source_value` | string | false | | | +| `updated_at` | string | false | | | + ## tailcfg.DERPMap ```json diff --git a/docs/api/templates.md b/docs/api/templates.md index d17f0e4ea0f1a..b17668cda1b1a 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -839,7 +839,51 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{id} \ To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. -## Get template version schema by template version ID +## Cancel template version by ID + +### Code samples + +```shell +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/templateversions/{id}/cancel \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /templateversions/{id}/cancel` + +### Parameters + +| Name | In | Type | Required | Description | +| ---- | ---- | ------------ | -------- | ------------------- | +| `id` | path | string(uuid) | true | Template version ID | + +### Example responses + +> 200 Response + +```json +{ + "detail": "string", + "message": "string", + "validations": [ + { + "detail": "string", + "field": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get template version schema by ID ### Code samples @@ -865,66 +909,44 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{id}/schema \ ```json [ { - "allow_override_destination": true, - "allow_override_source": true, - "created_at": "2019-08-24T14:15:22Z", - "default_destination_scheme": "none", - "default_refresh": "string", - "default_source_scheme": "none", - "default_source_value": "string", - "description": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "created_at": "string", + "default_source_value": true, + "destination_scheme": "string", + "id": "string", "name": "string", - "redisplay_value": true, - "validation_condition": "string", - "validation_contains": ["string"], - "validation_error": "string", - "validation_type_system": "string", - "validation_value_type": "string" + "schema_id": "string", + "scope": "string", + "scope_id": "string", + "source_scheme": "string", + "source_value": "string", + "updated_at": "string" } ] ``` ### Responses -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.ParameterSchema](schemas.md#codersdkparameterschema) | +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [parameter.ComputedValue](schemas.md#parametercomputedvalue) | -

Response Schema

+

Response Schema

Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------ | ------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» allow_override_destination` | boolean | false | | | -| `» allow_override_source` | boolean | false | | | -| `» created_at` | string | false | | | -| `» default_destination_scheme` | string | false | | | -| `» default_refresh` | string | false | | | -| `» default_source_scheme` | string | false | | | -| `» default_source_value` | string | false | | | -| `» description` | string | false | | | -| `» id` | string | false | | | -| `» job_id` | string | false | | | -| `» name` | string | false | | | -| `» redisplay_value` | boolean | false | | | -| `» validation_condition` | string | false | | | -| `» validation_contains` | array | false | | This is a special array of items provided if the validation condition
explicitly states the value must be one of a set. | -| `» validation_error` | string | false | | | -| `» validation_type_system` | string | false | | | -| `» validation_value_type` | string | false | | | - -#### Enumerated Values - -| Property | Value | -| ---------------------------- | ---------------------- | -| `default_destination_scheme` | `none` | -| `default_destination_scheme` | `environment_variable` | -| `default_destination_scheme` | `provisioner_variable` | -| `default_source_scheme` | `none` | -| `default_source_scheme` | `data` | +| Name | Type | Required | Restrictions | Description | +| ------------------------ | ------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string | false | | | +| `» default_source_value` | boolean | false | | | +| `» destination_scheme` | string | false | | | +| `» id` | string | false | | | +| `» name` | string | false | | | +| `» schema_id` | string | false | | | +| `» scope` | string | false | | | +| `» scope_id` | string | false | | | +| `» source_scheme` | string | false | | | +| `» source_value` | string | false | | | +| `» updated_at` | string | false | | | To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. From f96351a3b2c68d024dea27a33b6a822dc98eadb6 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 3 Jan 2023 14:27:43 +0100 Subject: [PATCH 13/61] templateversions --- coderd/apidoc/docs.go | 308 +++++++++++++++++++++++ coderd/apidoc/swagger.json | 280 +++++++++++++++++++++ coderd/templateversions.go | 70 ++++++ docs/api/schemas.md | 24 ++ docs/api/templates.md | 489 +++++++++++++++++++++++++++++++++++++ 5 files changed, 1171 insertions(+) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 34b0996f10a74..a03103999f70d 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1041,6 +1041,109 @@ const docTemplate = `{ } } }, + "/templateversions/{id}/dry-run": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Create template version dry-run", + "operationId": "create-template-version-dry-run", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Dry-run request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTemplateVersionDryRunRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.ProvisionerJob" + } + } + } + } + }, + "/templateversions/{id}/resources": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get template version logs by template version ID", + "operationId": "get-template-version-resources-by-template-version-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Before Unix timestamp", + "name": "before", + "in": "query" + }, + { + "type": "integer", + "description": "After Unix timestamp", + "name": "after", + "in": "query" + }, + { + "type": "boolean", + "description": "Follow log stream", + "name": "follow", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ProvisionerJobLog" + } + } + } + } + } + }, "/templateversions/{id}/schema": { "get": { "security": [ @@ -1079,6 +1182,197 @@ const docTemplate = `{ } } }, + "/templateversions/{templateversionid}/dry-run/{jobid}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get template version dry-run by job ID", + "operationId": "get-template-version-dry-run-by-job-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "templateversionid", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Job ID", + "name": "jobid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ProvisionerJob" + } + } + } + } + }, + "/templateversions/{templateversionid}/dry-run/{jobid}/cancel": { + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Cancel template version dry-run by job ID", + "operationId": "cancel-template-version-dry-run-by-job-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, + "/templateversions/{templateversionid}/dry-run/{jobid}/logs": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get template version dry-run logs by job ID", + "operationId": "get-template-version-dry-run-logs-by-job-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "templateversionid", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Job ID", + "name": "jobid", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Before Unix timestamp", + "name": "before", + "in": "query" + }, + { + "type": "integer", + "description": "After Unix timestamp", + "name": "after", + "in": "query" + }, + { + "type": "boolean", + "description": "Follow log stream", + "name": "follow", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ProvisionerJobLog" + } + } + } + } + } + }, + "/templateversions/{templateversionid}/dry-run/{jobid}/resources": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get template version dry-run resources by job ID", + "operationId": "get-template-version-dry-run-resources-by-job-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "templateversionid", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Job ID", + "name": "jobid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceResource" + } + } + } + } + } + }, "/updatecheck": { "get": { "produces": [ @@ -2447,6 +2741,20 @@ const docTemplate = `{ } } }, + "codersdk.CreateTemplateVersionDryRunRequest": { + "type": "object", + "properties": { + "parameter_values": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.CreateParameterRequest" + } + }, + "workspace_name": { + "type": "string" + } + } + }, "codersdk.CreateTestAuditLogRequest": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index ec759a22890dc..fb79b44971a4c 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -905,6 +905,99 @@ } } }, + "/templateversions/{id}/dry-run": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Create template version dry-run", + "operationId": "create-template-version-dry-run", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Dry-run request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTemplateVersionDryRunRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.ProvisionerJob" + } + } + } + } + }, + "/templateversions/{id}/resources": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get template version logs by template version ID", + "operationId": "get-template-version-resources-by-template-version-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Before Unix timestamp", + "name": "before", + "in": "query" + }, + { + "type": "integer", + "description": "After Unix timestamp", + "name": "after", + "in": "query" + }, + { + "type": "boolean", + "description": "Follow log stream", + "name": "follow", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ProvisionerJobLog" + } + } + } + } + } + }, "/templateversions/{id}/schema": { "get": { "security": [ @@ -939,6 +1032,179 @@ } } }, + "/templateversions/{templateversionid}/dry-run/{jobid}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get template version dry-run by job ID", + "operationId": "get-template-version-dry-run-by-job-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "templateversionid", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Job ID", + "name": "jobid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ProvisionerJob" + } + } + } + } + }, + "/templateversions/{templateversionid}/dry-run/{jobid}/cancel": { + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Cancel template version dry-run by job ID", + "operationId": "cancel-template-version-dry-run-by-job-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, + "/templateversions/{templateversionid}/dry-run/{jobid}/logs": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get template version dry-run logs by job ID", + "operationId": "get-template-version-dry-run-logs-by-job-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "templateversionid", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Job ID", + "name": "jobid", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Before Unix timestamp", + "name": "before", + "in": "query" + }, + { + "type": "integer", + "description": "After Unix timestamp", + "name": "after", + "in": "query" + }, + { + "type": "boolean", + "description": "Follow log stream", + "name": "follow", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ProvisionerJobLog" + } + } + } + } + } + }, + "/templateversions/{templateversionid}/dry-run/{jobid}/resources": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get template version dry-run resources by job ID", + "operationId": "get-template-version-dry-run-resources-by-job-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "templateversionid", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Job ID", + "name": "jobid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceResource" + } + } + } + } + } + }, "/updatecheck": { "get": { "produces": ["application/json"], @@ -2163,6 +2429,20 @@ } } }, + "codersdk.CreateTemplateVersionDryRunRequest": { + "type": "object", + "properties": { + "parameter_values": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.CreateParameterRequest" + } + }, + "workspace_name": { + "type": "string" + } + } + }, "codersdk.CreateTestAuditLogRequest": { "type": "object", "properties": { diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 594d86e9c8888..9a6dc0d817583 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -245,6 +245,16 @@ func (api *API) templateVersionParameters(rw http.ResponseWriter, r *http.Reques httpapi.Write(ctx, rw, http.StatusOK, values) } +// @Summary Create template version dry-run +// @ID create-template-version-dry-run +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Templates +// @Param id path string true "Template version ID" format(uuid) +// @Param request body codersdk.CreateTemplateVersionDryRunRequest true "Dry-run request" +// @Success 201 {object} codersdk.ProvisionerJob +// @Router /templateversions/{id}/dry-run [post] func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var ( @@ -340,6 +350,16 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques httpapi.Write(ctx, rw, http.StatusCreated, convertProvisionerJob(provisionerJob)) } +// @Summary Get template version dry-run by job ID +// @ID get-template-version-dry-run-by-job-id +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Templates +// @Param templateversionid path string true "Template version ID" format(uuid) +// @Param jobid path string true "Job ID" format(uuid) +// @Success 200 {object} codersdk.ProvisionerJob +// @Router /templateversions/{templateversionid}/dry-run/{jobid} [get] func (api *API) templateVersionDryRun(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() job, ok := api.fetchTemplateVersionDryRunJob(rw, r) @@ -350,6 +370,15 @@ func (api *API) templateVersionDryRun(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, convertProvisionerJob(job)) } +// @Summary Get template version dry-run resources by job ID +// @ID get-template-version-dry-run-resources-by-job-id +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param templateversionid path string true "Template version ID" format(uuid) +// @Param jobid path string true "Job ID" format(uuid) +// @Success 200 {array} codersdk.WorkspaceResource +// @Router /templateversions/{templateversionid}/dry-run/{jobid}/resources [get] func (api *API) templateVersionDryRunResources(rw http.ResponseWriter, r *http.Request) { job, ok := api.fetchTemplateVersionDryRunJob(rw, r) if !ok { @@ -359,6 +388,18 @@ func (api *API) templateVersionDryRunResources(rw http.ResponseWriter, r *http.R api.provisionerJobResources(rw, r, job) } +// @Summary Get template version dry-run logs by job ID +// @ID get-template-version-dry-run-logs-by-job-id +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param templateversionid path string true "Template version ID" format(uuid) +// @Param jobid path string true "Job ID" format(uuid) +// @Param before query int false "Before Unix timestamp" +// @Param after query int false "After Unix timestamp" +// @Param follow query bool false "Follow log stream" +// @Success 200 {array} codersdk.ProvisionerJobLog +// @Router /templateversions/{templateversionid}/dry-run/{jobid}/logs [get] func (api *API) templateVersionDryRunLogs(rw http.ResponseWriter, r *http.Request) { job, ok := api.fetchTemplateVersionDryRunJob(rw, r) if !ok { @@ -368,6 +409,14 @@ func (api *API) templateVersionDryRunLogs(rw http.ResponseWriter, r *http.Reques api.provisionerJobLogs(rw, r, job) } +// @Summary Cancel template version dry-run by job ID +// @ID cancel-template-version-dry-run-by-job-id +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param id path string true "Template version ID" format(uuid) +// @Success 200 {object} codersdk.Response +// @Router /templateversions/{templateversionid}/dry-run/{jobid}/cancel [patch] func (api *API) patchTemplateVersionDryRunCancel(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() templateVersion := httpmw.TemplateVersionParam(r) @@ -1126,6 +1175,15 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht httpapi.Write(ctx, rw, http.StatusCreated, convertTemplateVersion(templateVersion, convertProvisionerJob(provisionerJob), user)) } +// @Summary Get template version resources by template version ID +// @ID get-template-version-resources-by-template-version-id +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param id path string true "Template version ID" format(uuid) +// @Success 200 {array} codersdk.WorkspaceResource +// @Router /templateversions/{id}/resources [get] +// // templateVersionResources returns the workspace agent resources associated // with a template version. A template can specify more than one resource to be // provisioned, each resource can have an agent that dials back to coderd. The @@ -1154,6 +1212,18 @@ func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request api.provisionerJobResources(rw, r, job) } +// @Summary Get template version logs by template version ID +// @ID get-template-version-resources-by-template-version-id +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param id path string true "Template version ID" format(uuid) +// @Param before query int false "Before Unix timestamp" +// @Param after query int false "After Unix timestamp" +// @Param follow query bool false "Follow log stream" +// @Success 200 {array} codersdk.ProvisionerJobLog +// @Router /templateversions/{id}/resources [get] +// // templateVersionLogs returns the logs returned by the provisioner for the given // template version. These logs are only associated with the template version, // and not any build logs for a workspace. diff --git a/docs/api/schemas.md b/docs/api/schemas.md index ab8bfa6a5fb3a..c4f98b5cd5fa1 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -474,6 +474,30 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `template_version_id` | string | true | | Template version ID is an in-progress or completed job to use as an initial version of the template. | | This is required on creation to enable a user-flow of validating a template works. There is no reason the data-model cannot support empty templates, but it doesn't make sense for users. | +## codersdk.CreateTemplateVersionDryRunRequest + +```json +{ + "parameter_values": [ + { + "copy_from_parameter": "000e07d6-021d-446c-be14-48a9c20bca0b", + "destination_scheme": "none", + "name": "string", + "source_scheme": "none", + "source_value": "string" + } + ], + "workspace_name": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | --------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------- | +| `parameter_values` | array of [codersdk.CreateParameterRequest](#codersdkcreateparameterrequest) | false | | Parameter values is a structure used to create a new parameter value for a scope.] | +| `workspace_name` | string | false | | | + ## codersdk.CreateTestAuditLogRequest ```json diff --git a/docs/api/templates.md b/docs/api/templates.md index b17668cda1b1a..013c126d476e8 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -883,6 +883,145 @@ curl -X PATCH http://coder-server:8080/api/v2/templateversions/{id}/cancel \ To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. +## Create template version dry-run + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/templateversions/{id}/dry-run \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /templateversions/{id}/dry-run` + +> Body parameter + +```json +{ + "parameter_values": [ + { + "copy_from_parameter": "000e07d6-021d-446c-be14-48a9c20bca0b", + "destination_scheme": "none", + "name": "string", + "source_scheme": "none", + "source_value": "string" + } + ], + "workspace_name": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ---------------------------------------------------------------------------------------------------- | -------- | ------------------- | +| `id` | path | string(uuid) | true | Template version ID | +| `body` | body | [codersdk.CreateTemplateVersionDryRunRequest](schemas.md#codersdkcreatetemplateversiondryrunrequest) | true | Dry-run request | + +### Example responses + +> 201 Response + +```json +{ + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------ | ----------- | ------------------------------------------------------------ | +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.ProvisionerJob](schemas.md#codersdkprovisionerjob) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get template version logs by template version ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templateversions/{id}/resources \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templateversions/{id}/resources` + +### Parameters + +| Name | In | Type | Required | Description | +| -------- | ----- | ------------ | -------- | --------------------- | +| `id` | path | string(uuid) | true | Template version ID | +| `before` | query | integer | false | Before Unix timestamp | +| `after` | query | integer | false | After Unix timestamp | +| `follow` | query | boolean | false | Follow log stream | + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "2019-08-24T14:15:22Z", + "id": 0, + "log_level": "trace", + "log_source": "string", + "output": "string", + "stage": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.ProvisionerJobLog](schemas.md#codersdkprovisionerjoblog) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| -------------- | ------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string | false | | | +| `» id` | integer | false | | | +| `» log_level` | string | false | | | +| `» log_source` | string | false | | | +| `» output` | string | false | | | +| `» stage` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| ----------- | ------- | +| `log_level` | `trace` | +| `log_level` | `debug` | +| `log_level` | `info` | +| `log_level` | `warn` | +| `log_level` | `error` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + ## Get template version schema by ID ### Code samples @@ -950,3 +1089,353 @@ Status Code **200** | `» updated_at` | string | false | | | To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get template version dry-run by job ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversionid}/dry-run/{jobid} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templateversions/{templateversionid}/dry-run/{jobid}` + +### Parameters + +| Name | In | Type | Required | Description | +| ------------------- | ---- | ------------ | -------- | ------------------- | +| `templateversionid` | path | string(uuid) | true | Template version ID | +| `jobid` | path | string(uuid) | true | Job ID | + +### Example responses + +> 200 Response + +```json +{ + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ProvisionerJob](schemas.md#codersdkprovisionerjob) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Cancel template version dry-run by job ID + +### Code samples + +```shell +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversionid}/dry-run/{jobid}/cancel \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /templateversions/{templateversionid}/dry-run/{jobid}/cancel` + +### Parameters + +| Name | In | Type | Required | Description | +| ---- | ---- | ------------ | -------- | ------------------- | +| `id` | path | string(uuid) | true | Template version ID | + +### Example responses + +> 200 Response + +```json +{ + "detail": "string", + "message": "string", + "validations": [ + { + "detail": "string", + "field": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get template version dry-run logs by job ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversionid}/dry-run/{jobid}/logs \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templateversions/{templateversionid}/dry-run/{jobid}/logs` + +### Parameters + +| Name | In | Type | Required | Description | +| ------------------- | ----- | ------------ | -------- | --------------------- | +| `templateversionid` | path | string(uuid) | true | Template version ID | +| `jobid` | path | string(uuid) | true | Job ID | +| `before` | query | integer | false | Before Unix timestamp | +| `after` | query | integer | false | After Unix timestamp | +| `follow` | query | boolean | false | Follow log stream | + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "2019-08-24T14:15:22Z", + "id": 0, + "log_level": "trace", + "log_source": "string", + "output": "string", + "stage": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.ProvisionerJobLog](schemas.md#codersdkprovisionerjoblog) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| -------------- | ------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string | false | | | +| `» id` | integer | false | | | +| `» log_level` | string | false | | | +| `» log_source` | string | false | | | +| `» output` | string | false | | | +| `» stage` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| ----------- | ------- | +| `log_level` | `trace` | +| `log_level` | `debug` | +| `log_level` | `info` | +| `log_level` | `warn` | +| `log_level` | `error` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get template version dry-run resources by job ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversionid}/dry-run/{jobid}/resources \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templateversions/{templateversionid}/dry-run/{jobid}/resources` + +### Parameters + +| Name | In | Type | Required | Description | +| ------------------- | ---- | ------------ | -------- | ------------------- | +| `templateversionid` | path | string(uuid) | true | Template version ID | +| `jobid` | path | string(uuid) | true | Job ID | + +### Example responses + +> 200 Response + +```json +[ + { + "agents": [ + { + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "architecture": "string", + "connection_timeout_seconds": 0, + "created_at": "2019-08-24T14:15:22Z", + "directory": "string", + "disconnected_at": "2019-08-24T14:15:22Z", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "instance_id": "string", + "last_connected_at": "2019-08-24T14:15:22Z", + "latency": { + "property1": { + "latency_ms": 0, + "preferred": true + }, + "property2": { + "latency_ms": 0, + "preferred": true + } + }, + "name": "string", + "operating_system": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", + "startup_script": "string", + "status": "connecting", + "troubleshooting_url": "string", + "updated_at": "2019-08-24T14:15:22Z", + "version": "string" + } + ], + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "hide": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "metadata": [ + { + "key": "string", + "sensitive": true, + "value": "string" + } + ], + "name": "string", + "type": "string", + "workspace_transition": "start" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.WorkspaceResource](schemas.md#codersdkworkspaceresource) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | ---------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | DisplayName is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on
the client or not. | +| `»»» health` | string | false | | | +| `»»» healthcheck` | `codersdk.Healthcheck` | false | | | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies
an icon to be displayed in the dashboard. | +| `»»» id` | string | false | | | +| `»»» sharing_level` | string | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the
`coder server` or via a hostname-based dev URL. If this is set to true
and there is no app wildcard configured on the server, the app will not
be accessible in the UI. | +| `»»» url` | string | false | | URL is the address being proxied to inside the workspace.
If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» first_connected_at` | string | false | | | +| `»» id` | string | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string | false | | | +| `»» latency` | object | false | | DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | `codersdk.DERPRegion` | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» resource_id` | string | false | | | +| `»» startup_script` | string | false | | | +| `»» status` | string | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string | false | | | +| `»» version` | string | false | | | +| `» created_at` | string | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string | false | | | +| `» job_id` | string | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| ---------------------- | --------------- | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. From 15de3b6e8ea6c4f15ff747792689ddab77e9bdd4 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 3 Jan 2023 17:09:12 +0100 Subject: [PATCH 14/61] wip --- coderd/apidoc/docs.go | 108 +++++++++++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 93 ++++++++++++++++++++++++++++++++ coderd/organizations.go | 16 ++++++ codersdk/organizations.go | 6 +-- docs/api/organizations.md | 92 +++++++++++++++++++++++++++++++ docs/api/schemas.md | 34 ++++++++++++ docs/manifest.json | 4 ++ 7 files changed, 350 insertions(+), 3 deletions(-) create mode 100644 docs/api/organizations.md diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index a03103999f70d..173a04b9151a0 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -378,6 +378,77 @@ const docTemplate = `{ } } }, + "/organizations": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Organizations" + ], + "summary": "Create organization", + "operationId": "create-organization", + "parameters": [ + { + "description": "Create organization request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateOrganizationRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.Organization" + } + } + } + } + }, + "/organizations/{id}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Organizations" + ], + "summary": "Get organization by ID", + "operationId": "get-organization-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Organization" + } + } + } + } + }, "/organizations/{organization-id}/templates/": { "post": { "security": [ @@ -2660,6 +2731,17 @@ const docTemplate = `{ } } }, + "codersdk.CreateOrganizationRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + } + } + }, "codersdk.CreateParameterRequest": { "description": "CreateParameterRequest is a structure used to create a new parameter value for a scope.", "type": "object", @@ -3335,6 +3417,32 @@ const docTemplate = `{ } } }, + "codersdk.Organization": { + "type": "object", + "required": [ + "created_at", + "id", + "name", + "updated_at" + ], + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + } + }, "codersdk.Parameter": { "description": "Parameter represents a set value for the scope.", "type": "object", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index fb79b44971a4c..ebb867336dc27 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -324,6 +324,69 @@ } } }, + "/organizations": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Organizations"], + "summary": "Create organization", + "operationId": "create-organization", + "parameters": [ + { + "description": "Create organization request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateOrganizationRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.Organization" + } + } + } + } + }, + "/organizations/{id}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Organizations"], + "summary": "Get organization by ID", + "operationId": "get-organization-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Organization" + } + } + } + } + }, "/organizations/{organization-id}/templates/": { "post": { "security": [ @@ -2358,6 +2421,15 @@ } } }, + "codersdk.CreateOrganizationRequest": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string" + } + } + }, "codersdk.CreateParameterRequest": { "description": "CreateParameterRequest is a structure used to create a new parameter value for a scope.", "type": "object", @@ -3008,6 +3080,27 @@ } } }, + "codersdk.Organization": { + "type": "object", + "required": ["created_at", "id", "name", "updated_at"], + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + } + }, "codersdk.Parameter": { "description": "Parameter represents a set value for the scope.", "type": "object", diff --git a/coderd/organizations.go b/coderd/organizations.go index 27da001482f2d..ddbd38ac3d785 100644 --- a/coderd/organizations.go +++ b/coderd/organizations.go @@ -16,6 +16,14 @@ import ( "github.com/coder/coder/codersdk" ) +// @Summary Get organization by ID +// @ID get-organization-by-id +// @Security CoderSessionToken +// @Produce json +// @Tags Organizations +// @Param id path string true "Organization ID" format(uuid) +// @Success 200 {object} codersdk.Organization +// @Router /organizations/{id} [get] func (api *API) organization(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organization := httpmw.OrganizationParam(r) @@ -29,6 +37,14 @@ func (api *API) organization(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, convertOrganization(organization)) } +// @Summary Create organization +// @ID create-organization +// @Security CoderSessionToken +// @Produce json +// @Tags Organizations +// @Param request body codersdk.CreateOrganizationRequest true "Create organization request" +// @Success 201 {object} codersdk.Organization +// @Router /organizations [post] func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) diff --git a/codersdk/organizations.go b/codersdk/organizations.go index 2c15e78db1c69..39b50da013ec5 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -26,10 +26,10 @@ const ( // Organization is the JSON representation of a Coder organization. type Organization struct { - ID uuid.UUID `json:"id" validate:"required"` + ID uuid.UUID `json:"id" validate:"required" format:"uuid"` Name string `json:"name" validate:"required"` - CreatedAt time.Time `json:"created_at" validate:"required"` - UpdatedAt time.Time `json:"updated_at" validate:"required"` + CreatedAt time.Time `json:"created_at" validate:"required" format:"date-time"` + UpdatedAt time.Time `json:"updated_at" validate:"required" format:"date-time"` } // CreateTemplateVersionRequest enables callers to create a new Template Version. diff --git a/docs/api/organizations.md b/docs/api/organizations.md new file mode 100644 index 0000000000000..0fd429e8ceb03 --- /dev/null +++ b/docs/api/organizations.md @@ -0,0 +1,92 @@ +# Organizations + +> This page is incomplete, stay tuned. + +## Create organization + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/organizations \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /organizations` + +> Body parameter + +```json +{ + "name": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ---------------------------------------------------------------------------------- | -------- | --------------------------- | +| `body` | body | [codersdk.CreateOrganizationRequest](schemas.md#codersdkcreateorganizationrequest) | true | Create organization request | + +### Example responses + +> 201 Response + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "updated_at": "2019-08-24T14:15:22Z" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------ | ----------- | -------------------------------------------------------- | +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.Organization](schemas.md#codersdkorganization) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get organization by ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{id} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /organizations/{id}` + +### Parameters + +| Name | In | Type | Required | Description | +| ---- | ---- | ------------ | -------- | --------------- | +| `id` | path | string(uuid) | true | Organization ID | + +### Example responses + +> 200 Response + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "updated_at": "2019-08-24T14:15:22Z" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Organization](schemas.md#codersdkorganization) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/api/schemas.md b/docs/api/schemas.md index c4f98b5cd5fa1..90ed168c1f7e3 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -403,6 +403,20 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `external_url` | string | false | | External URL references the current Coder version. For production builds, this will link directly to a release. For development builds, this will link to a commit. | | `version` | string | false | | Version returns the semantic version of the build. | +## codersdk.CreateOrganizationRequest + +```json +{ + "name": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------ | ------ | -------- | ------------ | ----------- | +| `name` | string | true | | | + ## codersdk.CreateParameterRequest ```json @@ -2273,6 +2287,26 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `issuer_url` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | | `scopes` | [codersdk.DeploymentConfigField-array_string](#codersdkdeploymentconfigfield-array_string) | false | | | +## codersdk.Organization + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "updated_at": "2019-08-24T14:15:22Z" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------ | ------ | -------- | ------------ | ----------- | +| `created_at` | string | true | | | +| `id` | string | true | | | +| `name` | string | true | | | +| `updated_at` | string | true | | | + ## codersdk.Parameter ```json diff --git a/docs/manifest.json b/docs/manifest.json index 079735f2800bb..0d5a7ad0c4144 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -349,6 +349,10 @@ "title": "Files", "path": "./api/files.md" }, + { + "title": "Organizations", + "path": "./api/organizations.md" + }, { "title": "Templates", "path": "./api/templates.md" From eb4ba489511026f1502a023299dc0ea1563bffa2 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 3 Jan 2023 19:27:02 +0100 Subject: [PATCH 15/61] Merge --- coderd/apidoc/docs.go | 48 +------------------- coderd/apidoc/swagger.json | 44 +----------------- docs/api/builds.md | 92 +++++++++++++++++++------------------- docs/api/files.md | 17 ------- docs/api/parameters.md | 30 ------------- docs/api/schemas.md | 27 ----------- docs/manifest.json | 3 -- 7 files changed, 50 insertions(+), 211 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 86102cff2563b..1e00cda9d61ac 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -310,11 +310,7 @@ const docTemplate = `{ "CoderSessionToken": [] } ], -<<<<<<< HEAD - "description": "Notice: Swagger 2.0 doesn't support file upload with a ` + "`" + `content-type` + "`" + ` different than ` + "`" + `application/x-www-form-urlencoded` + "`" + `.", -======= "description": "Swagger notice: Swagger 2.0 doesn't support file upload with a ` + "`" + `content-type` + "`" + ` different than ` + "`" + `application/x-www-form-urlencoded` + "`" + `.", ->>>>>>> main "consumes": [ "application/x-tar" ], @@ -1852,7 +1848,6 @@ const docTemplate = `{ } } }, -<<<<<<< HEAD "codersdk.AWSInstanceIdentityToken": { "type": "object", "required": [ @@ -1920,8 +1915,6 @@ const docTemplate = `{ } } }, -======= ->>>>>>> main "codersdk.AuditDiff": { "type": "object", "additionalProperties": { @@ -2217,7 +2210,6 @@ const docTemplate = `{ } } }, -<<<<<<< HEAD "codersdk.CreateWorkspaceBuildRequest": { "type": "object", "required": [ @@ -2258,8 +2250,6 @@ const docTemplate = `{ } } }, -======= ->>>>>>> main "codersdk.DERP": { "type": "object", "properties": { @@ -2666,7 +2656,6 @@ const docTemplate = `{ } } }, -<<<<<<< HEAD "codersdk.GoogleInstanceIdentityToken": { "type": "object", "required": [ @@ -2678,8 +2667,6 @@ const docTemplate = `{ } } }, -======= ->>>>>>> main "codersdk.Healthcheck": { "type": "object", "properties": { @@ -2762,25 +2749,6 @@ const docTemplate = `{ "type": "object", "properties": { "created_at": { -<<<<<<< HEAD - "type": "string" - }, - "destination_scheme": { - "type": "string", - "enum": [ - "none", - "environment_variable", - "provisioner_variable" - ] - }, - "id": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, -======= "type": "string", "format": "date-time" }, @@ -2799,7 +2767,6 @@ const docTemplate = `{ "name": { "type": "string" }, ->>>>>>> main "scope": { "type": "string", "enum": [ @@ -2820,8 +2787,8 @@ const docTemplate = `{ ] }, "updated_at": { -<<<<<<< HEAD - "type": "string" + "type": "string", + "format": "date-time" } } }, @@ -2842,10 +2809,6 @@ const docTemplate = `{ "additionalProperties": { "type": "string" } -======= - "type": "string", - "format": "date-time" ->>>>>>> main } } }, @@ -3211,12 +3174,8 @@ const docTemplate = `{ "type": "object", "properties": { "hash": { -<<<<<<< HEAD - "type": "string" -======= "type": "string", "format": "uuid" ->>>>>>> main } } }, @@ -3712,7 +3671,6 @@ const docTemplate = `{ }, "netip.Addr": { "type": "object" -<<<<<<< HEAD }, "tailcfg.DERPMap": { "type": "object", @@ -3814,8 +3772,6 @@ const docTemplate = `{ "type": "string" } } -======= ->>>>>>> main } }, "securityDefinitions": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index d9523fa16c757..304cac3b05ee9 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -264,11 +264,7 @@ "CoderSessionToken": [] } ], -<<<<<<< HEAD - "description": "Notice: Swagger 2.0 doesn't support file upload with a `content-type` different than `application/x-www-form-urlencoded`.", -======= "description": "Swagger notice: Swagger 2.0 doesn't support file upload with a `content-type` different than `application/x-www-form-urlencoded`.", ->>>>>>> main "consumes": ["application/x-tar"], "produces": ["application/json"], "tags": ["Files"], @@ -1619,7 +1615,6 @@ } } }, -<<<<<<< HEAD "codersdk.AWSInstanceIdentityToken": { "type": "object", "required": ["document", "signature"], @@ -1684,8 +1679,6 @@ } } }, -======= ->>>>>>> main "codersdk.AuditDiff": { "type": "object", "additionalProperties": { @@ -1957,7 +1950,6 @@ } } }, -<<<<<<< HEAD "codersdk.CreateWorkspaceBuildRequest": { "type": "object", "required": ["transition"], @@ -1991,8 +1983,6 @@ } } }, -======= ->>>>>>> main "codersdk.DERP": { "type": "object", "properties": { @@ -2399,7 +2389,6 @@ } } }, -<<<<<<< HEAD "codersdk.GoogleInstanceIdentityToken": { "type": "object", "required": ["json_web_token"], @@ -2409,8 +2398,6 @@ } } }, -======= ->>>>>>> main "codersdk.Healthcheck": { "type": "object", "properties": { @@ -2493,21 +2480,6 @@ "type": "object", "properties": { "created_at": { -<<<<<<< HEAD - "type": "string" - }, - "destination_scheme": { - "type": "string", - "enum": ["none", "environment_variable", "provisioner_variable"] - }, - "id": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, -======= "type": "string", "format": "date-time" }, @@ -2522,7 +2494,6 @@ "name": { "type": "string" }, ->>>>>>> main "scope": { "type": "string", "enum": ["template", "workspace", "import_job"] @@ -2536,8 +2507,8 @@ "enum": ["none", "data"] }, "updated_at": { -<<<<<<< HEAD - "type": "string" + "type": "string", + "format": "date-time" } } }, @@ -2558,10 +2529,6 @@ "additionalProperties": { "type": "string" } -======= - "type": "string", - "format": "date-time" ->>>>>>> main } } }, @@ -2919,12 +2886,8 @@ "type": "object", "properties": { "hash": { -<<<<<<< HEAD - "type": "string" -======= "type": "string", "format": "uuid" ->>>>>>> main } } }, @@ -3391,7 +3354,6 @@ }, "netip.Addr": { "type": "object" -<<<<<<< HEAD }, "tailcfg.DERPMap": { "type": "object", @@ -3493,8 +3455,6 @@ "type": "string" } } -======= ->>>>>>> main } }, "securityDefinitions": { diff --git a/docs/api/builds.md b/docs/api/builds.md index 2eea39745c4ea..a58f9c0b61b3b 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -237,15 +237,15 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/log Status Code **200** -| Name | Type | Required | Restrictions | Description | -| -------------- | ------- | -------- | ------------ | ----------- | -| `[array item]` | array | false | | | -| `» created_at` | string | false | | | -| `» id` | integer | false | | | -| `» log_level` | string | false | | | -| `» log_source` | string | false | | | -| `» output` | string | false | | | -| `» stage` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| -------------- | ----------------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | false | | | +| `» id` | integer | false | | | +| `» log_level` | string | false | | | +| `» log_source` | string | false | | | +| `» output` | string | false | | | +| `» stage` | string | false | | | #### Enumerated Values @@ -383,40 +383,40 @@ Status Code **200** | `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | | `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | | `»»» icon` | string | false | | Icon is a relative path or external URL that specifies
an icon to be displayed in the dashboard. | -| `»»» id` | string | false | | | +| `»»» id` | string(uuid) | false | | | | `»»» sharing_level` | string | false | | | | `»»» slug` | string | false | | Slug is a unique identifier within the agent. | | `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the
`coder server` or via a hostname-based dev URL. If this is set to true
and there is no app wildcard configured on the server, the app will not
be accessible in the UI. | | `»»» url` | string | false | | URL is the address being proxied to inside the workspace.
If external is specified, this will be opened on the client. | | `»» architecture` | string | false | | | | `»» connection_timeout_seconds` | integer | false | | | -| `»» created_at` | string | false | | | +| `»» created_at` | string(date-time) | false | | | | `»» directory` | string | false | | | -| `»» disconnected_at` | string | false | | | +| `»» disconnected_at` | string(date-time) | false | | | | `»» environment_variables` | object | false | | | | `»»» [any property]` | string | false | | | -| `»» first_connected_at` | string | false | | | -| `»» id` | string | false | | | +| `»» first_connected_at` | string(date-time) | false | | | +| `»» id` | string(uuid) | false | | | | `»» instance_id` | string | false | | | -| `»» last_connected_at` | string | false | | | +| `»» last_connected_at` | string(date-time) | false | | | | `»» latency` | object | false | | DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). | | `»»» [any property]` | `codersdk.DERPRegion` | false | | | | `»»»» latency_ms` | number | false | | | | `»»»» preferred` | boolean | false | | | | `»» name` | string | false | | | | `»» operating_system` | string | false | | | -| `»» resource_id` | string | false | | | +| `»» resource_id` | string(uuid) | false | | | | `»» startup_script` | string | false | | | | `»» status` | string | false | | | | `»» troubleshooting_url` | string | false | | | -| `»» updated_at` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | | `»» version` | string | false | | | -| `» created_at` | string | false | | | +| `» created_at` | string(date-time) | false | | | | `» daily_cost` | integer | false | | | | `» hide` | boolean | false | | | | `» icon` | string | false | | | -| `» id` | string | false | | | -| `» job_id` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | | `» metadata` | array | false | | | | `»» key` | string | false | | | | `»» sensitive` | boolean | false | | | @@ -738,24 +738,24 @@ Status Code **200** | -------------------------------- | ------------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `[array item]` | array | false | | | | `» build_number` | integer | false | | | -| `» created_at` | string | false | | | +| `» created_at` | string(date-time) | false | | | | `» daily_cost` | integer | false | | | -| `» deadline` | string | false | | | -| `» id` | string | false | | | -| `» initiator_id` | string | false | | | +| `» deadline` | string(date-time) | false | | | +| `» id` | string(uuid) | false | | | +| `» initiator_id` | string(uuid) | false | | | | `» initiator_name` | string | false | | | | `» job` | `codersdk.ProvisionerJob` | false | | | -| `»» canceled_at` | string | false | | | -| `»» completed_at` | string | false | | | -| `»» created_at` | string | false | | | +| `»» canceled_at` | string(date-time) | false | | | +| `»» completed_at` | string(date-time) | false | | | +| `»» created_at` | string(date-time) | false | | | | `»» error` | string | false | | | -| `»» file_id` | string | false | | | -| `»» id` | string | false | | | -| `»» started_at` | string | false | | | +| `»» file_id` | string(uuid) | false | | | +| `»» id` | string(uuid) | false | | | +| `»» started_at` | string(date-time) | false | | | | `»» status` | string | false | | | | `»» tags` | object | false | | | | `»»» [any property]` | string | false | | | -| `»» worker_id` | string | false | | | +| `»» worker_id` | string(uuid) | false | | | | `» reason` | string | false | | | | `» resources` | array | false | | | | `»» agents` | array | false | | | @@ -769,40 +769,40 @@ Status Code **200** | `»»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | | `»»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | | `»»»» icon` | string | false | | Icon is a relative path or external URL that specifies
an icon to be displayed in the dashboard. | -| `»»»» id` | string | false | | | +| `»»»» id` | string(uuid) | false | | | | `»»»» sharing_level` | string | false | | | | `»»»» slug` | string | false | | Slug is a unique identifier within the agent. | | `»»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the
`coder server` or via a hostname-based dev URL. If this is set to true
and there is no app wildcard configured on the server, the app will not
be accessible in the UI. | | `»»»» url` | string | false | | URL is the address being proxied to inside the workspace.
If external is specified, this will be opened on the client. | | `»»» architecture` | string | false | | | | `»»» connection_timeout_seconds` | integer | false | | | -| `»»» created_at` | string | false | | | +| `»»» created_at` | string(date-time) | false | | | | `»»» directory` | string | false | | | -| `»»» disconnected_at` | string | false | | | +| `»»» disconnected_at` | string(date-time) | false | | | | `»»» environment_variables` | object | false | | | | `»»»» [any property]` | string | false | | | -| `»»» first_connected_at` | string | false | | | -| `»»» id` | string | false | | | +| `»»» first_connected_at` | string(date-time) | false | | | +| `»»» id` | string(uuid) | false | | | | `»»» instance_id` | string | false | | | -| `»»» last_connected_at` | string | false | | | +| `»»» last_connected_at` | string(date-time) | false | | | | `»»» latency` | object | false | | DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). | | `»»»» [any property]` | `codersdk.DERPRegion` | false | | | | `»»»»» latency_ms` | number | false | | | | `»»»»» preferred` | boolean | false | | | | `»»» name` | string | false | | | | `»»» operating_system` | string | false | | | -| `»»» resource_id` | string | false | | | +| `»»» resource_id` | string(uuid) | false | | | | `»»» startup_script` | string | false | | | | `»»» status` | string | false | | | | `»»» troubleshooting_url` | string | false | | | -| `»»» updated_at` | string | false | | | +| `»»» updated_at` | string(date-time) | false | | | | `»»» version` | string | false | | | -| `»» created_at` | string | false | | | +| `»» created_at` | string(date-time) | false | | | | `»» daily_cost` | integer | false | | | | `»» hide` | boolean | false | | | | `»» icon` | string | false | | | -| `»» id` | string | false | | | -| `»» job_id` | string | false | | | +| `»» id` | string(uuid) | false | | | +| `»» job_id` | string(uuid) | false | | | | `»» metadata` | array | false | | | | `»»» key` | string | false | | | | `»»» sensitive` | boolean | false | | | @@ -811,13 +811,13 @@ Status Code **200** | `»» type` | string | false | | | | `»» workspace_transition` | string | false | | | | `» status` | string | false | | | -| `» template_version_id` | string | false | | | +| `» template_version_id` | string(uuid) | false | | | | `» template_version_name` | string | false | | | | `» transition` | string | false | | | -| `» updated_at` | string | false | | | -| `» workspace_id` | string | false | | | +| `» updated_at` | string(date-time) | false | | | +| `» workspace_id` | string(uuid) | false | | | | `» workspace_name` | string | false | | | -| `» workspace_owner_id` | string | false | | | +| `» workspace_owner_id` | string(uuid) | false | | | | `» workspace_owner_name` | string | false | | | #### Enumerated Values diff --git a/docs/api/files.md b/docs/api/files.md index 8f576cff1ae45..c72493caabc71 100644 --- a/docs/api/files.md +++ b/docs/api/files.md @@ -16,11 +16,6 @@ curl -X POST http://coder-server:8080/api/v2/files \ `POST /files` -<<<<<<< HEAD -Notice: Swagger 2.0 doesn't support file upload with a `content-type` different than `application/x-www-form-urlencoded`. - -======= ->>>>>>> main > Body parameter ```yaml @@ -29,19 +24,11 @@ file: string ### Parameters -<<<<<<< HEAD -| Name | In | Type | Required | Description | -| -------------- | ------ | -------------- | -------- | ---------------------------------------- | -| `Content-Type` | header | string | true | Content-Type must be `application/x-tar` | -| `body` | body | object | true | | -| `» file` | body | string(binary) | true | File to be uploaded | -======= | Name | In | Type | Required | Description | | -------------- | ------ | ------ | -------- | ---------------------------------------- | | `Content-Type` | header | string | true | Content-Type must be `application/x-tar` | | `body` | body | object | true | | | `» file` | body | binary | true | File to be uploaded | ->>>>>>> main ### Example responses @@ -49,11 +36,7 @@ file: string ```json { -<<<<<<< HEAD - "hash": "string" -======= "hash": "19686d84-b10d-4f90-b18e-84fd3fa038fd" ->>>>>>> main } ``` diff --git a/docs/api/parameters.md b/docs/api/parameters.md index 809f4a575a00a..1612ac333a3df 100644 --- a/docs/api/parameters.md +++ b/docs/api/parameters.md @@ -37,22 +37,14 @@ curl -X GET http://coder-server:8080/api/v2/parameters/{scope}/{id} \ ```json [ { -<<<<<<< HEAD - "created_at": "string", -======= "created_at": "2019-08-24T14:15:22Z", ->>>>>>> main "destination_scheme": "none", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "name": "string", "scope": "template", "scope_id": "5d3fe357-12dd-4f62-b004-6d1fb3b8454f", "source_scheme": "none", -<<<<<<< HEAD - "updated_at": "string" -======= "updated_at": "2019-08-24T14:15:22Z" ->>>>>>> main } ] ``` @@ -67,19 +59,6 @@ curl -X GET http://coder-server:8080/api/v2/parameters/{scope}/{id} \ Status Code **200** -<<<<<<< HEAD -| Name | Type | Required | Restrictions | Description | -| ---------------------- | ------ | -------- | ------------ | ------------------------------------------------- | -| `[array item]` | array | false | | [Parameter represents a set value for the scope.] | -| `» created_at` | string | false | | | -| `» destination_scheme` | string | false | | | -| `» id` | string | false | | | -| `» name` | string | false | | | -| `» scope` | string | false | | | -| `» scope_id` | string | false | | | -| `» source_scheme` | string | false | | | -| `» updated_at` | string | false | | | -======= | Name | Type | Required | Restrictions | Description | | ---------------------- | ----------------- | -------- | ------------ | ------------------------------------------------- | | `[array item]` | array | false | | [Parameter represents a set value for the scope.] | @@ -91,7 +70,6 @@ Status Code **200** | `» scope_id` | string(uuid) | false | | | | `» source_scheme` | string | false | | | | `» updated_at` | string(date-time) | false | | | ->>>>>>> main #### Enumerated Values @@ -156,22 +134,14 @@ curl -X POST http://coder-server:8080/api/v2/parameters/{scope}/{id} \ ```json { -<<<<<<< HEAD - "created_at": "string", -======= "created_at": "2019-08-24T14:15:22Z", ->>>>>>> main "destination_scheme": "none", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "name": "string", "scope": "template", "scope_id": "5d3fe357-12dd-4f62-b004-6d1fb3b8454f", "source_scheme": "none", -<<<<<<< HEAD - "updated_at": "string" -======= "updated_at": "2019-08-24T14:15:22Z" ->>>>>>> main } ``` diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 54eb113339e6b..79cab87aef2c4 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -16,7 +16,6 @@ | ------------ | ------ | -------- | ------------ | ----------- | | `csp-report` | object | false | | | -<<<<<<< HEAD ## codersdk.AWSInstanceIdentityToken ```json @@ -91,8 +90,6 @@ | ----------------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------ | | `report_interval` | integer | false | | Report interval is the duration after which the agent should send stats again. | -======= ->>>>>>> main ## codersdk.AuditDiff ```json @@ -516,7 +513,6 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `resource_type` | `api_key` | | `resource_type` | `group` | -<<<<<<< HEAD ## codersdk.CreateWorkspaceBuildRequest ```json @@ -558,8 +554,6 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `transition` | `stop` | | `transition` | `delete` | -======= ->>>>>>> main ## codersdk.DERP ```json @@ -1923,7 +1917,6 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `type` | string | false | | | | `validate_url` | string | false | | | -<<<<<<< HEAD ## codersdk.GoogleInstanceIdentityToken ```json @@ -1938,8 +1931,6 @@ CreateParameterRequest is a structure used to create a new parameter value for a | ---------------- | ------ | -------- | ------------ | ----------- | | `json_web_token` | string | true | | | -======= ->>>>>>> main ## codersdk.Healthcheck ```json @@ -2246,22 +2237,14 @@ CreateParameterRequest is a structure used to create a new parameter value for a ```json { -<<<<<<< HEAD - "created_at": "string", -======= "created_at": "2019-08-24T14:15:22Z", ->>>>>>> main "destination_scheme": "none", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "name": "string", "scope": "template", "scope_id": "5d3fe357-12dd-4f62-b004-6d1fb3b8454f", "source_scheme": "none", -<<<<<<< HEAD - "updated_at": "string" -======= "updated_at": "2019-08-24T14:15:22Z" ->>>>>>> main } ``` @@ -2293,7 +2276,6 @@ Parameter represents a set value for the scope. | `source_scheme` | `none` | | `source_scheme` | `data` | -<<<<<<< HEAD ## codersdk.PostWorkspaceAgentVersionRequest ```json @@ -2326,8 +2308,6 @@ Parameter represents a set value for the scope. | `healths` | object | false | | Healths is a map of the workspace app name and the health of the app. | | » `[any property]` | string | false | | | -======= ->>>>>>> main ## codersdk.PprofConfig ```json @@ -3002,11 +2982,7 @@ Parameter represents a set value for the scope. ```json { -<<<<<<< HEAD - "hash": "string" -======= "hash": "19686d84-b10d-4f90-b18e-84fd3fa038fd" ->>>>>>> main } ``` @@ -3913,7 +3889,6 @@ Parameter represents a set value for the scope. ### Properties _None_ -<<<<<<< HEAD ## tailcfg.DERPMap @@ -4063,5 +4038,3 @@ It corresponds to the legacy derpN.tailscale.com hostnames used by older clients RegionIDs must be non-zero, positive, and guaranteed to fit in a JavaScript number. RegionIDs in range 900-999 are reserved for end users to run their own DERP nodes.| |`regionName`|string|false||Regionname is a long English name for the region: "New York City", "San Francisco", "Singapore", "Frankfurt", etc.| -======= ->>>>>>> main diff --git a/docs/manifest.json b/docs/manifest.json index f71a99d85f2e8..079735f2800bb 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -362,7 +362,6 @@ "path": "./api/parameters.md" }, { -<<<<<<< HEAD "title": "Agents", "path": "./api/agents.md" }, @@ -371,8 +370,6 @@ "path": "./api/builds.md" }, { -======= ->>>>>>> main "title": "Schemas", "path": "./api/schemas.md" } From e314c243f9de2add15ea51601390e9aa30f59528 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 4 Jan 2023 11:29:50 +0100 Subject: [PATCH 16/61] x-apidocgen --- coderd/apidoc/docs.go | 3 +++ coderd/apidoc/swagger.json | 3 +++ coderd/workspaceagents.go | 1 + 3 files changed, 7 insertions(+) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 1e00cda9d61ac..907ea117126b1 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1205,6 +1205,9 @@ const docTemplate = `{ "200": { "description": "OK" } + }, + "x-apidocgen": { + "skip": true } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 304cac3b05ee9..e78c98d53b608 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1041,6 +1041,9 @@ "200": { "description": "OK" } + }, + "x-apidocgen": { + "skip": true } } }, diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 10586ed7a37ba..f078655b37b07 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -154,6 +154,7 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) // @Param request body codersdk.PostWorkspaceAgentVersionRequest true "Version request" // @Success 200 // @Router /workspaceagents/me/version [post] +// @x-apidocgen {"skip": true} func (api *API) postWorkspaceAgentVersion(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgent(r) From 97cd7ace66bcf132d87d592714f684a3f57e5f99 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 4 Jan 2023 11:43:22 +0100 Subject: [PATCH 17/61] NullTime hack not needed anymore --- scripts/apidocgen/markdown-template/main.dot | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/apidocgen/markdown-template/main.dot b/scripts/apidocgen/markdown-template/main.dot index 59236d94a9fbd..0fd9e25421288 100644 --- a/scripts/apidocgen/markdown-template/main.dot +++ b/scripts/apidocgen/markdown-template/main.dot @@ -92,11 +92,7 @@ > This page is incomplete, stay tuned. -{{ for (var s in data.components.schemas) { - if (s == "codersdk.NullTime") { - continue; - } -}} +{{ for (var s in data.components.schemas) { }} {{ var origSchema = data.components.schemas[s]; }} {{ var schema = data.api.components.schemas[s]; }} From ae06598c9f37fed78427efe22dffe93c100053c3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 4 Jan 2023 12:27:06 +0100 Subject: [PATCH 18/61] Fix: x-apidocgen --- coderd/apidoc/docs.go | 1 + coderd/apidoc/swagger.json | 1 + codersdk/workspaceagents.go | 1 + docs/api/agents.md | 35 ------------------- docs/api/schemas.md | 14 -------- scripts/apidocgen/markdown-template/main.dot | 11 ++++-- .../apidocgen/markdown-template/operation.dot | 6 ++++ 7 files changed, 17 insertions(+), 52 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 907ea117126b1..41aeca263f6b0 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2796,6 +2796,7 @@ const docTemplate = `{ } }, "codersdk.PostWorkspaceAgentVersionRequest": { + "description": "x-apidocgen:skip", "type": "object", "properties": { "version": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index e78c98d53b608..d9c4cd7be78c6 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -2516,6 +2516,7 @@ } }, "codersdk.PostWorkspaceAgentVersionRequest": { + "description": "x-apidocgen:skip", "type": "object", "properties": { "version": { diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index cd4c237616607..bb8ce62406f12 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -115,6 +115,7 @@ type WorkspaceAgentConnectionInfo struct { } // @typescript-ignore PostWorkspaceAgentVersionRequest +// @Description x-apidocgen:skip type PostWorkspaceAgentVersionRequest struct { Version string `json:"version"` } diff --git a/docs/api/agents.md b/docs/api/agents.md index d001d5309d67c..8ff2ee9036d2a 100644 --- a/docs/api/agents.md +++ b/docs/api/agents.md @@ -437,38 +437,3 @@ curl -X POST http://coder-server:8080/api/v2/workspaceagents/me/report-stats \ | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.AgentStatsResponse](schemas.md#codersdkagentstatsresponse) | To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. - -## Submit workspace agent version - -### Code samples - -```shell -# Example request using curl -curl -X POST http://coder-server:8080/api/v2/workspaceagents/me/version \ - -H 'Content-Type: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`POST /workspaceagents/me/version` - -> Body parameter - -```json -{ - "version": "string" -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -| ------ | ---- | ------------------------------------------------------------------------------------------------ | -------- | --------------- | -| `body` | body | [codersdk.PostWorkspaceAgentVersionRequest](schemas.md#codersdkpostworkspaceagentversionrequest) | true | Version request | - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ------ | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | - -To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 79cab87aef2c4..2e3d91cf5de22 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2276,20 +2276,6 @@ Parameter represents a set value for the scope. | `source_scheme` | `none` | | `source_scheme` | `data` | -## codersdk.PostWorkspaceAgentVersionRequest - -```json -{ - "version": "string" -} -``` - -### Properties - -| Name | Type | Required | Restrictions | Description | -| --------- | ------ | -------- | ------------ | ----------- | -| `version` | string | false | | | - ## codersdk.PostWorkspaceAppHealthsRequest ```json diff --git a/scripts/apidocgen/markdown-template/main.dot b/scripts/apidocgen/markdown-template/main.dot index 0fd9e25421288..f2eea883eff57 100644 --- a/scripts/apidocgen/markdown-template/main.dot +++ b/scripts/apidocgen/markdown-template/main.dot @@ -92,9 +92,14 @@ > This page is incomplete, stay tuned. -{{ for (var s in data.components.schemas) { }} -{{ var origSchema = data.components.schemas[s]; }} -{{ var schema = data.api.components.schemas[s]; }} +{{ for (var s in data.components.schemas) { + var origSchema = data.components.schemas[s]; + var schema = data.api.components.schemas[s]; + + if (schema.description && schema.description.startsWith("x-apidocgen:skip")) { + continue; + } +}} {{= data.tags.section }} ## {{=s}} diff --git a/scripts/apidocgen/markdown-template/operation.dot b/scripts/apidocgen/markdown-template/operation.dot index 8e8129757c843..57856444b3eb7 100644 --- a/scripts/apidocgen/markdown-template/operation.dot +++ b/scripts/apidocgen/markdown-template/operation.dot @@ -18,6 +18,12 @@ ## {{= data.operationUniqueName}} +{{ + if (data.operation['x-apidocgen'] && data.operation['x-apidocgen'].skip) { + return ""; + } +}} + {{ data.methodUpper = data.method.verb.toUpperCase(); }} {{ data.url = data.utils.slashes(data.baseUrl + data.method.path); }} {{ data.parameters = data.operation.parameters; }} From 7aa0f65857611cbc64c5491908529c21c3aa9a18 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 4 Jan 2023 16:09:27 +0100 Subject: [PATCH 19/61] Members --- coderd/apidoc/docs.go | 335 +++++++++++++++++++++++++++-- coderd/apidoc/swagger.json | 311 +++++++++++++++++++++++++-- coderd/members.go | 11 + coderd/organizations.go | 4 +- coderd/roles.go | 9 + coderd/templates.go | 18 +- coderd/templateversions.go | 35 +++- coderd/workspaces.go | 7 +- codersdk/organizationmember.go | 8 +- docs/api/members.md | 112 ++++++++++ docs/api/organizations.md | 10 +- docs/api/schemas.md | 85 ++++++++ docs/api/templates.md | 370 ++++++++++++++++++++++++++------- docs/api/workspaces.md | 10 +- docs/manifest.json | 8 +- 15 files changed, 1195 insertions(+), 138 deletions(-) create mode 100644 docs/api/members.md diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 60ecf9c4e9845..e58a7edd1150f 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -414,7 +414,7 @@ const docTemplate = `{ } } }, - "/organizations/{id}": { + "/organizations/{organization}": { "get": { "security": [ { @@ -434,7 +434,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Organization ID", - "name": "id", + "name": "organization", "in": "path", "required": true } @@ -449,8 +449,46 @@ const docTemplate = `{ } } }, - "/organizations/{organization-id}/templates/": { - "post": { + "/organizations/{organization}/members/roles": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Members" + ], + "summary": "Get member roles by organization", + "operationId": "get-member-roles-by-organization", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.AssignableRoles" + } + } + } + } + } + }, + "/organizations/{organization}/members/{user}/roles": { + "put": { "security": [ { "CoderSessionToken": [] @@ -463,24 +501,31 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Templates" + "Members" ], - "summary": "Create template by organization", - "operationId": "create-template-by-organization", + "summary": "Assign role to organization member", + "operationId": "assign-role-to-organization-member", "parameters": [ { - "description": "Request body", + "description": "Update roles request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateTemplateRequest" + "$ref": "#/definitions/codersdk.UpdateRoles" } }, { "type": "string", "description": "Organization ID", - "name": "organization-id", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Username, UUID, or me", + "name": "user", "in": "path", "required": true } @@ -489,7 +534,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Template" + "$ref": "#/definitions/codersdk.OrganizationMember" } } } @@ -508,8 +553,8 @@ const docTemplate = `{ "tags": [ "Workspaces" ], - "summary": "Create workspace by organization", - "operationId": "create-workspace-by-organization", + "summary": "Create user workspace by organization", + "operationId": "create-user-workspace-by-organization", "parameters": [ { "type": "string", @@ -521,7 +566,7 @@ const docTemplate = `{ }, { "type": "string", - "description": "Username", + "description": "Username, UUID, or me", "name": "user", "in": "path", "required": true @@ -573,9 +618,91 @@ const docTemplate = `{ } } } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Create template by organization", + "operationId": "create-template-by-organization", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTemplateRequest" + } + }, + { + "type": "string", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Template" + } + } + } + } + }, + "/organizations/{organization}/templates/examples": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get template examples by organization", + "operationId": "get-template-examples-by-organization", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateExample" + } + } + } + } } }, - "/organizations/{organization}/templates/{template-name}": { + "/organizations/{organization}/templates/{templatename}": { "get": { "security": [ { @@ -602,7 +729,7 @@ const docTemplate = `{ { "type": "string", "description": "Template name", - "name": "template-name", + "name": "templatename", "in": "path", "required": true } @@ -617,6 +744,95 @@ const docTemplate = `{ } } }, + "/organizations/{organization}/templateversions": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Create template version by organization", + "operationId": "create-template-version-by-organization", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "description": "Create template version request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTemplateVersionDryRunRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + }, + "/organizations/{organization}/templateversions/{templateversionname}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get previous template version by organization and name", + "operationId": "get-previous-template-version-by-organization-and-name", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Template version name", + "name": "templateversionname", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + }, "/parameters/{scope}/{id}": { "get": { "security": [ @@ -997,7 +1213,7 @@ const docTemplate = `{ } } }, - "/templates/{id}/versions/{name}": { + "/templates/{id}/versions/{templateversionname}": { "get": { "security": [ { @@ -1023,8 +1239,8 @@ const docTemplate = `{ }, { "type": "string", - "description": "Template name", - "name": "name", + "description": "Template version name", + "name": "templateversionname", "in": "path", "required": true } @@ -2554,6 +2770,20 @@ const docTemplate = `{ } } }, + "codersdk.AssignableRoles": { + "type": "object", + "properties": { + "assignable": { + "type": "boolean" + }, + "display_name": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, "codersdk.AuditDiff": { "type": "object", "additionalProperties": { @@ -3446,6 +3676,33 @@ const docTemplate = `{ } } }, + "codersdk.OrganizationMember": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "organization_id": { + "type": "string", + "format": "uuid" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Role" + } + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "user_id": { + "type": "string", + "format": "uuid" + } + } + }, "codersdk.Parameter": { "description": "Parameter represents a set value for the scope.", "type": "object", @@ -3888,6 +4145,35 @@ const docTemplate = `{ } } }, + "codersdk.TemplateExample": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "id": { + "type": "string" + }, + "markdown": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "url": { + "type": "string" + } + } + }, "codersdk.TemplateVersion": { "type": "object", "properties": { @@ -3980,6 +4266,17 @@ const docTemplate = `{ } } }, + "codersdk.UpdateRoles": { + "type": "object", + "properties": { + "roles": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "codersdk.UpdateWorkspaceAutostartRequest": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index a898bcc954c3a..70af1a888e6f5 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -356,7 +356,7 @@ } } }, - "/organizations/{id}": { + "/organizations/{organization}": { "get": { "security": [ { @@ -372,7 +372,7 @@ "type": "string", "format": "uuid", "description": "Organization ID", - "name": "id", + "name": "organization", "in": "path", "required": true } @@ -387,8 +387,42 @@ } } }, - "/organizations/{organization-id}/templates/": { - "post": { + "/organizations/{organization}/members/roles": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Members"], + "summary": "Get member roles by organization", + "operationId": "get-member-roles-by-organization", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.AssignableRoles" + } + } + } + } + } + }, + "/organizations/{organization}/members/{user}/roles": { + "put": { "security": [ { "CoderSessionToken": [] @@ -396,23 +430,30 @@ ], "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Templates"], - "summary": "Create template by organization", - "operationId": "create-template-by-organization", + "tags": ["Members"], + "summary": "Assign role to organization member", + "operationId": "assign-role-to-organization-member", "parameters": [ { - "description": "Request body", + "description": "Update roles request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateTemplateRequest" + "$ref": "#/definitions/codersdk.UpdateRoles" } }, { "type": "string", "description": "Organization ID", - "name": "organization-id", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Username, UUID, or me", + "name": "user", "in": "path", "required": true } @@ -421,7 +462,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Template" + "$ref": "#/definitions/codersdk.OrganizationMember" } } } @@ -436,8 +477,8 @@ ], "produces": ["application/json"], "tags": ["Workspaces"], - "summary": "Create workspace by organization", - "operationId": "create-workspace-by-organization", + "summary": "Create user workspace by organization", + "operationId": "create-user-workspace-by-organization", "parameters": [ { "type": "string", @@ -449,7 +490,7 @@ }, { "type": "string", - "description": "Username", + "description": "Username, UUID, or me", "name": "user", "in": "path", "required": true @@ -497,9 +538,81 @@ } } } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Create template by organization", + "operationId": "create-template-by-organization", + "parameters": [ + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTemplateRequest" + } + }, + { + "type": "string", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Template" + } + } + } + } + }, + "/organizations/{organization}/templates/examples": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get template examples by organization", + "operationId": "get-template-examples-by-organization", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateExample" + } + } + } + } } }, - "/organizations/{organization}/templates/{template-name}": { + "/organizations/{organization}/templates/{templatename}": { "get": { "security": [ { @@ -522,7 +635,7 @@ { "type": "string", "description": "Template name", - "name": "template-name", + "name": "templatename", "in": "path", "required": true } @@ -537,6 +650,85 @@ } } }, + "/organizations/{organization}/templateversions": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Create template version by organization", + "operationId": "create-template-version-by-organization", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "description": "Create template version request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTemplateVersionDryRunRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + }, + "/organizations/{organization}/templateversions/{templateversionname}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get previous template version by organization and name", + "operationId": "get-previous-template-version-by-organization-and-name", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Template version name", + "name": "templateversionname", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + }, "/parameters/{scope}/{id}": { "get": { "security": [ @@ -865,7 +1057,7 @@ } } }, - "/templates/{id}/versions/{name}": { + "/templates/{id}/versions/{templateversionname}": { "get": { "security": [ { @@ -887,8 +1079,8 @@ }, { "type": "string", - "description": "Template name", - "name": "name", + "description": "Template version name", + "name": "templateversionname", "in": "path", "required": true } @@ -2252,6 +2444,20 @@ } } }, + "codersdk.AssignableRoles": { + "type": "object", + "properties": { + "assignable": { + "type": "boolean" + }, + "display_name": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, "codersdk.AuditDiff": { "type": "object", "additionalProperties": { @@ -3104,6 +3310,33 @@ } } }, + "codersdk.OrganizationMember": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "organization_id": { + "type": "string", + "format": "uuid" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Role" + } + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "user_id": { + "type": "string", + "format": "uuid" + } + } + }, "codersdk.Parameter": { "description": "Parameter represents a set value for the scope.", "type": "object", @@ -3520,6 +3753,35 @@ } } }, + "codersdk.TemplateExample": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "id": { + "type": "string" + }, + "markdown": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "url": { + "type": "string" + } + } + }, "codersdk.TemplateVersion": { "type": "object", "properties": { @@ -3610,6 +3872,17 @@ } } }, + "codersdk.UpdateRoles": { + "type": "object", + "properties": { + "roles": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "codersdk.UpdateWorkspaceAutostartRequest": { "type": "object", "properties": { diff --git a/coderd/members.go b/coderd/members.go index 2ae7fa9de6469..9141cadb6a0b3 100644 --- a/coderd/members.go +++ b/coderd/members.go @@ -16,6 +16,17 @@ import ( "github.com/coder/coder/codersdk" ) +// @Summary Assign role to organization member +// @ID assign-role-to-organization-member +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Members +// @Param request body codersdk.UpdateRoles true "Update roles request" +// @Param organization path string true "Organization ID" +// @Param user path string true "Username, UUID, or me" +// @Success 200 {object} codersdk.OrganizationMember +// @Router /organizations/{organization}/members/{user}/roles [put] func (api *API) putMemberRoles(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/coderd/organizations.go b/coderd/organizations.go index ddbd38ac3d785..84bf1197348dd 100644 --- a/coderd/organizations.go +++ b/coderd/organizations.go @@ -21,9 +21,9 @@ import ( // @Security CoderSessionToken // @Produce json // @Tags Organizations -// @Param id path string true "Organization ID" format(uuid) +// @Param organization path string true "Organization ID" format(uuid) // @Success 200 {object} codersdk.Organization -// @Router /organizations/{id} [get] +// @Router /organizations/{organization} [get] func (api *API) organization(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organization := httpmw.OrganizationParam(r) diff --git a/coderd/roles.go b/coderd/roles.go index 4e5ae05d99470..9eb1518d9ec6e 100644 --- a/coderd/roles.go +++ b/coderd/roles.go @@ -23,6 +23,15 @@ func (api *API) assignableSiteRoles(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, assignableRoles(actorRoles.Roles, roles)) } +// @Summary Get member roles by organization +// @ID get-member-roles-by-organization +// @Security CoderSessionToken +// @Produce json +// @Tags Members +// @Param organization path string true "Organization ID" format(uuid) +// @Success 200 {array} codersdk.AssignableRoles +// @Router /organizations/{organization}/members/roles [get] +// // assignableSiteRoles returns all site wide roles that can be assigned. func (api *API) assignableOrgRoles(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/coderd/templates.go b/coderd/templates.go index a7680355ee05d..0f2f25008383d 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -149,9 +149,9 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) { // @Produce json // @Tags Templates // @Param request body codersdk.CreateTemplateRequest true "Request body" -// @Param organization-id path string true "Organization ID" +// @Param organization path string true "Organization ID" // @Success 200 {object} codersdk.Template -// @Router /organizations/{organization-id}/templates/ [post] +// @Router /organizations/{organization}/templates [post] // Returns a single template. // Create a new template in an organization. func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Request) { @@ -347,7 +347,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque // @Produce json // @Tags Templates // @Param organization path string true "Organization ID" format(uuid) -// @Success 200 {object} []codersdk.Template +// @Success 200 {array} codersdk.Template // @Router /organizations/{organization}/templates [get] func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -413,9 +413,9 @@ func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request) // @Produce json // @Tags Templates // @Param organization path string true "Organization ID" format(uuid) -// @Param template-name path string true "Template name" +// @Param templatename path string true "Template name" // @Success 200 {object} codersdk.Template -// @Router /organizations/{organization}/templates/{template-name} [get] +// @Router /organizations/{organization}/templates/{templatename} [get] func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organization := httpmw.OrganizationParam(r) @@ -625,6 +625,14 @@ func (api *API) templateDAUs(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, resp) } +// @Summary Get template examples by organization +// @ID get-template-examples-by-organization +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param organization path string true "Organization ID" format(uuid) +// @Success 200 {array} codersdk.TemplateExample +// @Router /organizations/{organization}/templates/examples [get] func (api *API) templateExamples(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 9a6dc0d817583..dfff89b56f5a6 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -646,9 +646,9 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque // @Produce json // @Tags Templates // @Param id path string true "Template ID" format(uuid) -// @Param name path string true "Template name" +// @Param templateversionname path string true "Template version name" // @Success 200 {array} codersdk.TemplateVersion -// @Router /templates/{id}/versions/{name} [get] +// @Router /templates/{id}/versions/{templateversionname} [get] func (api *API) templateVersionByName(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() template := httpmw.TemplateParam(r) @@ -699,6 +699,15 @@ func (api *API) templateVersionByName(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, convertTemplateVersion(templateVersion, convertProvisionerJob(job), user)) } +// @Summary Get template version by organization and name +// @ID get-template-version-by-organization-and-name +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param organization path string true "Organization ID" format(uuid) +// @Param templateversionname path string true "Template version name" +// @Success 200 {object} codersdk.TemplateVersion +// @Router /organizations/{organization}/templateversions/{templateversionname} [get] func (api *API) templateVersionByOrganizationAndName(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organization := httpmw.OrganizationParam(r) @@ -741,6 +750,15 @@ func (api *API) templateVersionByOrganizationAndName(rw http.ResponseWriter, r * httpapi.Write(ctx, rw, http.StatusOK, convertTemplateVersion(templateVersion, convertProvisionerJob(job), user)) } +// @Summary Get previous template version by organization and name +// @ID get-previous-template-version-by-organization-and-name +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param organization path string true "Organization ID" format(uuid) +// @Param templateversionname path string true "Template version name" +// @Success 200 {object} codersdk.TemplateVersion +// @Router /organizations/{organization}/templateversions/{templateversionname} [get] func (api *API) previousTemplateVersionByOrganizationAndName(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organization := httpmw.OrganizationParam(r) @@ -890,7 +908,18 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque }) } -// Creates a new version of a template. An import job is queued to parse the storage method provided. +// @Summary Create template version by organization +// @ID create-template-version-by-organization +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Templates +// @Param organization path string true "Organization ID" format(uuid) +// @Param request body codersdk.CreateTemplateVersionDryRunRequest true "Create template version request" +// @Success 201 {object} codersdk.TemplateVersion +// @Router /organizations/{organization}/templateversions [post] +// +// postTemplateVersionsByOrganization creates a new version of a template. An import job is queued to parse the storage method provided. func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 23e605b298de1..6edd0d13c629b 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -266,15 +266,16 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) )) } -// @Summary Create workspace by organization -// @ID create-workspace-by-organization +// @Summary Create user workspace by organization +// @ID create-user-workspace-by-organization // @Security CoderSessionToken // @Produce json // @Tags Workspaces // @Param organization path string true "Organization ID" format(uuid) -// @Param user path string true "Username" +// @Param user path string true "Username, UUID, or me" // @Success 200 {object} codersdk.Workspace // @Router /organizations/{organization}/members/{user}/workspaces [post] +// // Create a new workspace for the currently authenticated user. func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Request) { var ( diff --git a/codersdk/organizationmember.go b/codersdk/organizationmember.go index e35280ffa7e75..4ec90fb29cbde 100644 --- a/codersdk/organizationmember.go +++ b/codersdk/organizationmember.go @@ -7,9 +7,9 @@ import ( ) type OrganizationMember struct { - UserID uuid.UUID `db:"user_id" json:"user_id"` - OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + UserID uuid.UUID `db:"user_id" json:"user_id" format:"uuid"` + OrganizationID uuid.UUID `db:"organization_id" json:"organization_id" format:"uuid"` + CreatedAt time.Time `db:"created_at" json:"created_at" format:"date-time"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at" format:"date-time"` Roles []Role `db:"roles" json:"roles"` } diff --git a/docs/api/members.md b/docs/api/members.md new file mode 100644 index 0000000000000..519101c732979 --- /dev/null +++ b/docs/api/members.md @@ -0,0 +1,112 @@ +# Members + +> This page is incomplete, stay tuned. + +## Get member roles by organization + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/members/roles \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /organizations/{organization}/members/roles` + +### Parameters + +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------ | -------- | --------------- | +| `organization` | path | string(uuid) | true | Organization ID | + +### Example responses + +> 200 Response + +```json +[ + { + "assignable": true, + "display_name": "string", + "name": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.AssignableRoles](schemas.md#codersdkassignableroles) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| ---------------- | ------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» assignable` | boolean | false | | | +| `» display_name` | string | false | | | +| `» name` | string | false | | | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Assign role to organization member + +### Code samples + +```shell +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/organizations/{organization}/members/{user}/roles \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /organizations/{organization}/members/{user}/roles` + +> Body parameter + +```json +{ + "roles": ["string"] +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------------------------------------------------ | -------- | --------------------- | +| `organization` | path | string | true | Organization ID | +| `user` | path | string | true | Username, UUID, or me | +| `body` | body | [codersdk.UpdateRoles](schemas.md#codersdkupdateroles) | true | Update roles request | + +### Example responses + +> 200 Response + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "updated_at": "2019-08-24T14:15:22Z", + "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.OrganizationMember](schemas.md#codersdkorganizationmember) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/api/organizations.md b/docs/api/organizations.md index 0fd429e8ceb03..bc0c6cbd07c1d 100644 --- a/docs/api/organizations.md +++ b/docs/api/organizations.md @@ -57,18 +57,18 @@ To perform this operation, you must be authenticated by means of one of the foll ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{id} \ +curl -X GET http://coder-server:8080/api/v2/organizations/{organization} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /organizations/{id}` +`GET /organizations/{organization}` ### Parameters -| Name | In | Type | Required | Description | -| ---- | ---- | ------------ | -------- | --------------- | -| `id` | path | string(uuid) | true | Organization ID | +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------ | -------- | --------------- | +| `organization` | path | string(uuid) | true | Organization ID | ### Example responses diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 64f9a2077d564..87cffd52a8d78 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -90,6 +90,24 @@ | ----------------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------ | | `report_interval` | integer | false | | Report interval is the duration after which the agent should send stats again. | +## codersdk.AssignableRoles + +```json +{ + "assignable": true, + "display_name": "string", + "name": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------------- | ------- | -------- | ------------ | ----------- | +| `assignable` | boolean | false | | | +| `display_name` | string | false | | | +| `name` | string | false | | | + ## codersdk.AuditDiff ```json @@ -2307,6 +2325,33 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `name` | string | true | | | | `updated_at` | string | true | | | +## codersdk.OrganizationMember + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "updated_at": "2019-08-24T14:15:22Z", + "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------------- | --------------------------------------- | -------- | ------------ | ----------- | +| `created_at` | string | false | | | +| `organization_id` | string | false | | | +| `roles` | array of [codersdk.Role](#codersdkrole) | false | | | +| `updated_at` | string | false | | | +| `user_id` | string | false | | | + ## codersdk.Parameter ```json @@ -2989,6 +3034,32 @@ Parameter represents a set value for the scope. | --------- | ----------------------------------------------- | -------- | ------------ | ----------- | | `entries` | array of [codersdk.DAUEntry](#codersdkdauentry) | false | | | +## codersdk.TemplateExample + +```json +{ + "description": "string", + "icon": "string", + "id": "string", + "markdown": "string", + "name": "string", + "tags": ["string"], + "url": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------- | --------------- | -------- | ------------ | ----------- | +| `description` | string | false | | | +| `icon` | string | false | | | +| `id` | string | false | | | +| `markdown` | string | false | | | +| `name` | string | false | | | +| `tags` | array of string | false | | | +| `url` | string | false | | | + ## codersdk.TemplateVersion ```json @@ -3144,6 +3215,20 @@ Parameter represents a set value for the scope. | `url` | string | false | | URL to download the latest release of Coder. | | `version` | string | false | | Version is the semantic version for the latest release of Coder. | +## codersdk.UpdateRoles + +```json +{ + "roles": ["string"] +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------- | --------------- | -------- | ------------ | ----------- | +| `roles` | array of string | false | | | + ## codersdk.UpdateWorkspaceAutostartRequest ```json diff --git a/docs/api/templates.md b/docs/api/templates.md index b5b618f7860d3..cb6b84a975890 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -2,19 +2,111 @@ > This page is incomplete, stay tuned. +## Get templates by organization + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templates \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /organizations/{organization}/templates` + +### Parameters + +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------ | -------- | --------------- | +| `organization` | path | string(uuid) | true | Organization ID | + +### Example responses + +> 200 Response + +```json +[ + { + "active_user_count": 0, + "active_version_id": "string", + "allow_user_cancel_workspace_jobs": true, + "build_time_stats": { + "property1": { + "p50": 123, + "p95": 146 + }, + "property2": { + "p50": 123, + "p95": 146 + } + }, + "created_at": "2019-08-24T14:15:22Z", + "created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f", + "created_by_name": "string", + "default_ttl_ms": 0, + "description": "string", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "provisioner": "string", + "updated_at": "2019-08-24T14:15:22Z", + "workspace_owner_count": 0 + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Template](schemas.md#codersdktemplate) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| ------------------------------------ | --------------------------------- | -------- | ------------ | ------------------------------------------ | +| `[array item]` | array | false | | | +| `» active_user_count` | integer | false | | ActiveUserCount is set to -1 when loading. | +| `» active_version_id` | string | false | | | +| `» allow_user_cancel_workspace_jobs` | boolean | false | | | +| `» build_time_stats` | `codersdk.TemplateBuildTimeStats` | false | | | +| `»» [any property]` | `codersdk.TransitionStats` | false | | | +| `»»» p50` | integer | false | | | +| `»»» p95` | integer | false | | | +| `» created_at` | string(date-time) | false | | | +| `» created_by_id` | string(uuid) | false | | | +| `» created_by_name` | string | false | | | +| `» default_ttl_ms` | integer | false | | | +| `» description` | string | false | | | +| `» display_name` | string | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» name` | string | false | | | +| `» organization_id` | string(uuid) | false | | | +| `» provisioner` | string | false | | | +| `» updated_at` | string(date-time) | false | | | +| `» workspace_owner_count` | integer | false | | | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + ## Create template by organization ### Code samples ```shell # Example request using curl -curl -X POST http://coder-server:8080/api/v2/organizations/{organization-id}/templates/ \ +curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templates \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /organizations/{organization-id}/templates/` +`POST /organizations/{organization}/templates` > Body parameter @@ -41,10 +133,10 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization-id}/tem ### Parameters -| Name | In | Type | Required | Description | -| ----------------- | ---- | -------------------------------------------------------------------------- | -------- | --------------- | -| `organization-id` | path | string | true | Organization ID | -| `body` | body | [codersdk.CreateTemplateRequest](schemas.md#codersdkcreatetemplaterequest) | true | Request body | +| Name | In | Type | Required | Description | +| -------------- | ---- | -------------------------------------------------------------------------- | -------- | --------------- | +| `organization` | path | string | true | Organization ID | +| `body` | body | [codersdk.CreateTemplateRequest](schemas.md#codersdkcreatetemplaterequest) | true | Request body | ### Example responses @@ -89,18 +181,18 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization-id}/tem To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. -## Get templates by organization +## Get template examples by organization ### Code samples ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templates \ +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templates/examples \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /organizations/{organization}/templates` +`GET /organizations/{organization}/templates/examples` ### Parameters @@ -115,69 +207,37 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat ```json [ { - "active_user_count": 0, - "active_version_id": "string", - "allow_user_cancel_workspace_jobs": true, - "build_time_stats": { - "property1": { - "p50": 123, - "p95": 146 - }, - "property2": { - "p50": 123, - "p95": 146 - } - }, - "created_at": "2019-08-24T14:15:22Z", - "created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f", - "created_by_name": "string", - "default_ttl_ms": 0, "description": "string", - "display_name": "string", "icon": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "id": "string", + "markdown": "string", "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "provisioner": "string", - "updated_at": "2019-08-24T14:15:22Z", - "workspace_owner_count": 0 + "tags": ["string"], + "url": "string" } ] ``` ### Responses -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Template](schemas.md#codersdktemplate) | +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.TemplateExample](schemas.md#codersdktemplateexample) | -

Response Schema

+

Response Schema

Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------------ | --------------------------------- | -------- | ------------ | ------------------------------------------ | -| `[array item]` | array | false | | | -| `» active_user_count` | integer | false | | ActiveUserCount is set to -1 when loading. | -| `» active_version_id` | string | false | | | -| `» allow_user_cancel_workspace_jobs` | boolean | false | | | -| `» build_time_stats` | `codersdk.TemplateBuildTimeStats` | false | | | -| `»» [any property]` | `codersdk.TransitionStats` | false | | | -| `»»» p50` | integer | false | | | -| `»»» p95` | integer | false | | | -| `» created_at` | string(date-time) | false | | | -| `» created_by_id` | string(uuid) | false | | | -| `» created_by_name` | string | false | | | -| `» default_ttl_ms` | integer | false | | | -| `» description` | string | false | | | -| `» display_name` | string | false | | | -| `» icon` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» name` | string | false | | | -| `» organization_id` | string(uuid) | false | | | -| `» provisioner` | string | false | | | -| `» updated_at` | string(date-time) | false | | | -| `» workspace_owner_count` | integer | false | | | +| Name | Type | Required | Restrictions | Description | +| --------------- | ------ | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» description` | string | false | | | +| `» icon` | string | false | | | +| `» id` | string | false | | | +| `» markdown` | string | false | | | +| `» name` | string | false | | | +| `» tags` | array | false | | | +| `» url` | string | false | | | To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. @@ -187,19 +247,19 @@ To perform this operation, you must be authenticated by means of one of the foll ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templates/{template-name} \ +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templates/{templatename} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /organizations/{organization}/templates/{template-name}` +`GET /organizations/{organization}/templates/{templatename}` ### Parameters -| Name | In | Type | Required | Description | -| --------------- | ---- | ------------ | -------- | --------------- | -| `organization` | path | string(uuid) | true | Organization ID | -| `template-name` | path | string | true | Template name | +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------ | -------- | --------------- | +| `organization` | path | string(uuid) | true | Organization ID | +| `templatename` | path | string | true | Template name | ### Example responses @@ -244,6 +304,174 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. +## Create template version by organization + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templateversions \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /organizations/{organization}/templateversions` + +> Body parameter + +```json +{ + "parameter_values": [ + { + "copy_from_parameter": "000e07d6-021d-446c-be14-48a9c20bca0b", + "destination_scheme": "none", + "name": "string", + "source_scheme": "none", + "source_value": "string" + } + ], + "workspace_name": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| -------------- | ---- | ---------------------------------------------------------------------------------------------------- | -------- | ------------------------------- | +| `organization` | path | string(uuid) | true | Organization ID | +| `body` | body | [codersdk.CreateTemplateVersionDryRunRequest](schemas.md#codersdkcreatetemplateversiondryrunrequest) | true | Create template version request | + +### Example responses + +> 201 Response + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "created_by": { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + }, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job": { + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" + }, + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "readme": "string", + "template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc", + "updated_at": "2019-08-24T14:15:22Z" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------ | ----------- | -------------------------------------------------------------- | +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.TemplateVersion](schemas.md#codersdktemplateversion) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get previous template version by organization and name + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templateversions/{templateversionname} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /organizations/{organization}/templateversions/{templateversionname}` + +### Parameters + +| Name | In | Type | Required | Description | +| --------------------- | ---- | ------------ | -------- | --------------------- | +| `organization` | path | string(uuid) | true | Organization ID | +| `templateversionname` | path | string | true | Template version name | + +### Example responses + +> 200 Response + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "created_by": { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + }, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job": { + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" + }, + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "readme": "string", + "template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc", + "updated_at": "2019-08-24T14:15:22Z" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.TemplateVersion](schemas.md#codersdktemplateversion) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + ## Get template metadata by ID ### Code samples @@ -642,19 +870,19 @@ To perform this operation, you must be authenticated by means of one of the foll ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/templates/{id}/versions/{name} \ +curl -X GET http://coder-server:8080/api/v2/templates/{id}/versions/{templateversionname} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /templates/{id}/versions/{name}` +`GET /templates/{id}/versions/{templateversionname}` ### Parameters -| Name | In | Type | Required | Description | -| ------ | ---- | ------------ | -------- | ------------- | -| `id` | path | string(uuid) | true | Template ID | -| `name` | path | string | true | Template name | +| Name | In | Type | Required | Description | +| --------------------- | ---- | ------------ | -------- | --------------------- | +| `id` | path | string(uuid) | true | Template ID | +| `templateversionname` | path | string | true | Template version name | ### Example responses diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 9254214759b09..7220e8ccd754e 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -2,7 +2,7 @@ > This page is incomplete, stay tuned. -## Create workspace by organization +## Create user workspace by organization ### Code samples @@ -17,10 +17,10 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member ### Parameters -| Name | In | Type | Required | Description | -| -------------- | ---- | ------------ | -------- | --------------- | -| `organization` | path | string(uuid) | true | Organization ID | -| `user` | path | string | true | Username | +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------ | -------- | --------------------- | +| `organization` | path | string(uuid) | true | Organization ID | +| `user` | path | string | true | Username, UUID, or me | ### Example responses diff --git a/docs/manifest.json b/docs/manifest.json index 0d5a7ad0c4144..603771bedcb0b 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -354,13 +354,17 @@ "path": "./api/organizations.md" }, { - "title": "Templates", - "path": "./api/templates.md" + "title": "Members", + "path": "./api/members.md" }, { "title": "Workspaces", "path": "./api/workspaces.md" }, + { + "title": "Templates", + "path": "./api/templates.md" + }, { "title": "Parameters", "path": "./api/parameters.md" From b93398ec51b79f43efa10b4109f16b8d151c9e10 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 4 Jan 2023 16:14:58 +0100 Subject: [PATCH 20/61] Fixes --- coderd/apidoc/docs.go | 246 +++++++++++---- coderd/apidoc/swagger.json | 230 ++++++++++---- coderd/templateversions.go | 34 +- docs/api/templates.md | 628 ++++++++++++++++++++++++++++--------- 4 files changed, 855 insertions(+), 283 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index e58a7edd1150f..2429df3b8232a 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -792,6 +792,48 @@ const docTemplate = `{ } }, "/organizations/{organization}/templateversions/{templateversionname}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get template version by organization and name", + "operationId": "get-template-version-by-organization-and-name", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Template version name", + "name": "templateversionname", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + }, + "/organizations/{organization}/templateversions/{templateversionname}/previous": { "get": { "security": [ { @@ -1375,64 +1417,54 @@ const docTemplate = `{ } } }, - "/templateversions/{id}/resources": { + "/templateversions/{templateversionid}/dry-run/{jobid}": { "get": { "security": [ { "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ "Templates" ], - "summary": "Get template version logs by template version ID", - "operationId": "get-template-version-resources-by-template-version-id", + "summary": "Get template version dry-run by job ID", + "operationId": "get-template-version-dry-run-by-job-id", "parameters": [ { "type": "string", "format": "uuid", "description": "Template version ID", - "name": "id", + "name": "templateversionid", "in": "path", "required": true }, { - "type": "integer", - "description": "Before Unix timestamp", - "name": "before", - "in": "query" - }, - { - "type": "integer", - "description": "After Unix timestamp", - "name": "after", - "in": "query" - }, - { - "type": "boolean", - "description": "Follow log stream", - "name": "follow", - "in": "query" + "type": "string", + "format": "uuid", + "description": "Job ID", + "name": "jobid", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.ProvisionerJobLog" - } + "$ref": "#/definitions/codersdk.ProvisionerJob" } } } } }, - "/templateversions/{id}/schema": { - "get": { + "/templateversions/{templateversionid}/dry-run/{jobid}/cancel": { + "patch": { "security": [ { "CoderSessionToken": [] @@ -1444,8 +1476,8 @@ const docTemplate = `{ "tags": [ "Templates" ], - "summary": "Get template version schema by ID", - "operationId": "get-template-version-schema-by-id", + "summary": "Cancel template version dry-run by job ID", + "operationId": "cancel-template-version-dry-run-by-job-id", "parameters": [ { "type": "string", @@ -1460,33 +1492,27 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/parameter.ComputedValue" - } + "$ref": "#/definitions/codersdk.Response" } } } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}": { + "/templateversions/{templateversionid}/dry-run/{jobid}/logs": { "get": { "security": [ { "CoderSessionToken": [] } ], - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], "tags": [ "Templates" ], - "summary": "Get template version dry-run by job ID", - "operationId": "get-template-version-dry-run-by-job-id", + "summary": "Get template version dry-run logs by job ID", + "operationId": "get-template-version-dry-run-logs-by-job-id", "parameters": [ { "type": "string", @@ -1503,20 +1529,41 @@ const docTemplate = `{ "name": "jobid", "in": "path", "required": true + }, + { + "type": "integer", + "description": "Before Unix timestamp", + "name": "before", + "in": "query" + }, + { + "type": "integer", + "description": "After Unix timestamp", + "name": "after", + "in": "query" + }, + { + "type": "boolean", + "description": "Follow log stream", + "name": "follow", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ProvisionerJob" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ProvisionerJobLog" + } } } } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}/cancel": { - "patch": { + "/templateversions/{templateversionid}/dry-run/{jobid}/resources": { + "get": { "security": [ { "CoderSessionToken": [] @@ -1528,14 +1575,22 @@ const docTemplate = `{ "tags": [ "Templates" ], - "summary": "Cancel template version dry-run by job ID", - "operationId": "cancel-template-version-dry-run-by-job-id", + "summary": "Get template version dry-run resources by job ID", + "operationId": "get-template-version-dry-run-resources-by-job-id", "parameters": [ { "type": "string", "format": "uuid", "description": "Template version ID", - "name": "id", + "name": "templateversionid", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Job ID", + "name": "jobid", "in": "path", "required": true } @@ -1544,13 +1599,16 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceResource" + } } } } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}/logs": { + "/templateversions/{templateversion}/logs": { "get": { "security": [ { @@ -1563,22 +1621,14 @@ const docTemplate = `{ "tags": [ "Templates" ], - "summary": "Get template version dry-run logs by job ID", - "operationId": "get-template-version-dry-run-logs-by-job-id", + "summary": "Get logs by template version", + "operationId": "get-logs-by-template-version", "parameters": [ { "type": "string", "format": "uuid", "description": "Template version ID", - "name": "templateversionid", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "Job ID", - "name": "jobid", + "name": "templateversion", "in": "path", "required": true }, @@ -1614,7 +1664,7 @@ const docTemplate = `{ } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}/resources": { + "/templateversions/{templateversion}/parameters": { "get": { "security": [ { @@ -1627,22 +1677,52 @@ const docTemplate = `{ "tags": [ "Templates" ], - "summary": "Get template version dry-run resources by job ID", - "operationId": "get-template-version-dry-run-resources-by-job-id", + "summary": "Get parameters by template version", + "operationId": "get-parameters-by-template-version", "parameters": [ { "type": "string", "format": "uuid", "description": "Template version ID", - "name": "templateversionid", + "name": "templateversion", "in": "path", "required": true - }, + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/parameter.ComputedValue" + } + } + } + } + } + }, + "/templateversions/{templateversion}/resources": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get resources by template version", + "operationId": "get-resources-by-template-version", + "parameters": [ { "type": "string", "format": "uuid", - "description": "Job ID", - "name": "jobid", + "description": "Template version ID", + "name": "templateversion", "in": "path", "required": true } @@ -1660,6 +1740,44 @@ const docTemplate = `{ } } }, + "/templateversions/{templateversion}/schema": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get schema by template version", + "operationId": "get-schema-by-template-version", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "templateversion", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ParameterSchema" + } + } + } + } + } + }, "/updatecheck": { "get": { "produces": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 70af1a888e6f5..d600ce601784a 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -692,6 +692,44 @@ } }, "/organizations/{organization}/templateversions/{templateversionname}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get template version by organization and name", + "operationId": "get-template-version-by-organization-and-name", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Template version name", + "name": "templateversionname", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + }, + "/organizations/{organization}/templateversions/{templateversionname}/previous": { "get": { "security": [ { @@ -1201,60 +1239,48 @@ } } }, - "/templateversions/{id}/resources": { + "/templateversions/{templateversionid}/dry-run/{jobid}": { "get": { "security": [ { "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Templates"], - "summary": "Get template version logs by template version ID", - "operationId": "get-template-version-resources-by-template-version-id", + "summary": "Get template version dry-run by job ID", + "operationId": "get-template-version-dry-run-by-job-id", "parameters": [ { "type": "string", "format": "uuid", "description": "Template version ID", - "name": "id", + "name": "templateversionid", "in": "path", "required": true }, { - "type": "integer", - "description": "Before Unix timestamp", - "name": "before", - "in": "query" - }, - { - "type": "integer", - "description": "After Unix timestamp", - "name": "after", - "in": "query" - }, - { - "type": "boolean", - "description": "Follow log stream", - "name": "follow", - "in": "query" + "type": "string", + "format": "uuid", + "description": "Job ID", + "name": "jobid", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.ProvisionerJobLog" - } + "$ref": "#/definitions/codersdk.ProvisionerJob" } } } } }, - "/templateversions/{id}/schema": { - "get": { + "/templateversions/{templateversionid}/dry-run/{jobid}/cancel": { + "patch": { "security": [ { "CoderSessionToken": [] @@ -1262,8 +1288,8 @@ ], "produces": ["application/json"], "tags": ["Templates"], - "summary": "Get template version schema by ID", - "operationId": "get-template-version-schema-by-id", + "summary": "Cancel template version dry-run by job ID", + "operationId": "cancel-template-version-dry-run-by-job-id", "parameters": [ { "type": "string", @@ -1278,27 +1304,23 @@ "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/parameter.ComputedValue" - } + "$ref": "#/definitions/codersdk.Response" } } } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}": { + "/templateversions/{templateversionid}/dry-run/{jobid}/logs": { "get": { "security": [ { "CoderSessionToken": [] } ], - "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Templates"], - "summary": "Get template version dry-run by job ID", - "operationId": "get-template-version-dry-run-by-job-id", + "summary": "Get template version dry-run logs by job ID", + "operationId": "get-template-version-dry-run-logs-by-job-id", "parameters": [ { "type": "string", @@ -1315,20 +1337,41 @@ "name": "jobid", "in": "path", "required": true + }, + { + "type": "integer", + "description": "Before Unix timestamp", + "name": "before", + "in": "query" + }, + { + "type": "integer", + "description": "After Unix timestamp", + "name": "after", + "in": "query" + }, + { + "type": "boolean", + "description": "Follow log stream", + "name": "follow", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.ProvisionerJob" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ProvisionerJobLog" + } } } } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}/cancel": { - "patch": { + "/templateversions/{templateversionid}/dry-run/{jobid}/resources": { + "get": { "security": [ { "CoderSessionToken": [] @@ -1336,14 +1379,22 @@ ], "produces": ["application/json"], "tags": ["Templates"], - "summary": "Cancel template version dry-run by job ID", - "operationId": "cancel-template-version-dry-run-by-job-id", + "summary": "Get template version dry-run resources by job ID", + "operationId": "get-template-version-dry-run-resources-by-job-id", "parameters": [ { "type": "string", "format": "uuid", "description": "Template version ID", - "name": "id", + "name": "templateversionid", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Job ID", + "name": "jobid", "in": "path", "required": true } @@ -1352,13 +1403,16 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceResource" + } } } } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}/logs": { + "/templateversions/{templateversion}/logs": { "get": { "security": [ { @@ -1367,22 +1421,14 @@ ], "produces": ["application/json"], "tags": ["Templates"], - "summary": "Get template version dry-run logs by job ID", - "operationId": "get-template-version-dry-run-logs-by-job-id", + "summary": "Get logs by template version", + "operationId": "get-logs-by-template-version", "parameters": [ { "type": "string", "format": "uuid", "description": "Template version ID", - "name": "templateversionid", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "uuid", - "description": "Job ID", - "name": "jobid", + "name": "templateversion", "in": "path", "required": true }, @@ -1418,7 +1464,7 @@ } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}/resources": { + "/templateversions/{templateversion}/parameters": { "get": { "security": [ { @@ -1427,22 +1473,48 @@ ], "produces": ["application/json"], "tags": ["Templates"], - "summary": "Get template version dry-run resources by job ID", - "operationId": "get-template-version-dry-run-resources-by-job-id", + "summary": "Get parameters by template version", + "operationId": "get-parameters-by-template-version", "parameters": [ { "type": "string", "format": "uuid", "description": "Template version ID", - "name": "templateversionid", + "name": "templateversion", "in": "path", "required": true - }, + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/parameter.ComputedValue" + } + } + } + } + } + }, + "/templateversions/{templateversion}/resources": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get resources by template version", + "operationId": "get-resources-by-template-version", + "parameters": [ { "type": "string", "format": "uuid", - "description": "Job ID", - "name": "jobid", + "description": "Template version ID", + "name": "templateversion", "in": "path", "required": true } @@ -1460,6 +1532,40 @@ } } }, + "/templateversions/{templateversion}/schema": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get schema by template version", + "operationId": "get-schema-by-template-version", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "templateversion", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ParameterSchema" + } + } + } + } + } + }, "/updatecheck": { "get": { "produces": ["application/json"], diff --git a/coderd/templateversions.go b/coderd/templateversions.go index dfff89b56f5a6..e606d170a6601 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -132,14 +132,14 @@ func (api *API) patchCancelTemplateVersion(rw http.ResponseWriter, r *http.Reque }) } -// @Summary Get template version schema by template version ID -// @ID get-template-version-schema-by-template-version-id +// @Summary Get schema by template version +// @ID get-schema-by-template-version // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template version ID" format(uuid) +// @Param templateversion path string true "Template version ID" format(uuid) // @Success 200 {array} codersdk.ParameterSchema -// @Router /templateversions/{id}/schema [get] +// @Router /templateversions/{templateversion}/schema [get] func (api *API) templateVersionSchema(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var ( @@ -192,14 +192,14 @@ func (api *API) templateVersionSchema(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, apiSchemas) } -// @Summary Get template version schema by ID -// @ID get-template-version-schema-by-id +// @Summary Get parameters by template version +// @ID get-parameters-by-template-version // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template version ID" format(uuid) +// @Param templateversion path string true "Template version ID" format(uuid) // @Success 200 {array} parameter.ComputedValue -// @Router /templateversions/{id}/schema [get] +// @Router /templateversions/{templateversion}/parameters [get] func (api *API) templateVersionParameters(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var ( @@ -758,7 +758,7 @@ func (api *API) templateVersionByOrganizationAndName(rw http.ResponseWriter, r * // @Param organization path string true "Organization ID" format(uuid) // @Param templateversionname path string true "Template version name" // @Success 200 {object} codersdk.TemplateVersion -// @Router /organizations/{organization}/templateversions/{templateversionname} [get] +// @Router /organizations/{organization}/templateversions/{templateversionname}/previous [get] func (api *API) previousTemplateVersionByOrganizationAndName(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organization := httpmw.OrganizationParam(r) @@ -1204,14 +1204,14 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht httpapi.Write(ctx, rw, http.StatusCreated, convertTemplateVersion(templateVersion, convertProvisionerJob(provisionerJob), user)) } -// @Summary Get template version resources by template version ID -// @ID get-template-version-resources-by-template-version-id +// @Summary Get resources by template version +// @ID get-resources-by-template-version // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template version ID" format(uuid) +// @Param templateversion path string true "Template version ID" format(uuid) // @Success 200 {array} codersdk.WorkspaceResource -// @Router /templateversions/{id}/resources [get] +// @Router /templateversions/{templateversion}/resources [get] // // templateVersionResources returns the workspace agent resources associated // with a template version. A template can specify more than one resource to be @@ -1241,17 +1241,17 @@ func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request api.provisionerJobResources(rw, r, job) } -// @Summary Get template version logs by template version ID -// @ID get-template-version-resources-by-template-version-id +// @Summary Get logs by template version +// @ID get-logs-by-template-version // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template version ID" format(uuid) +// @Param templateversion path string true "Template version ID" format(uuid) // @Param before query int false "Before Unix timestamp" // @Param after query int false "After Unix timestamp" // @Param follow query bool false "Follow log stream" // @Success 200 {array} codersdk.ProvisionerJobLog -// @Router /templateversions/{id}/resources [get] +// @Router /templateversions/{templateversion}/logs [get] // // templateVersionLogs returns the logs returned by the provisioner for the given // template version. These logs are only associated with the template version, diff --git a/docs/api/templates.md b/docs/api/templates.md index cb6b84a975890..e7c586d459b90 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -397,7 +397,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. -## Get previous template version by organization and name +## Get template version by organization and name ### Code samples @@ -472,6 +472,81 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. +## Get previous template version by organization and name + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templateversions/{templateversionname}/previous \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /organizations/{organization}/templateversions/{templateversionname}/previous` + +### Parameters + +| Name | In | Type | Required | Description | +| --------------------- | ---- | ------------ | -------- | --------------------- | +| `organization` | path | string(uuid) | true | Organization ID | +| `templateversionname` | path | string | true | Template version name | + +### Example responses + +> 200 Response + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "created_by": { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + }, + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job": { + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" + }, + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "readme": "string", + "template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc", + "updated_at": "2019-08-24T14:15:22Z" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.TemplateVersion](schemas.md#codersdktemplateversion) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + ## Get template metadata by ID ### Code samples @@ -1179,145 +1254,6 @@ curl -X POST http://coder-server:8080/api/v2/templateversions/{id}/dry-run \ To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. -## Get template version logs by template version ID - -### Code samples - -```shell -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/templateversions/{id}/resources \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /templateversions/{id}/resources` - -### Parameters - -| Name | In | Type | Required | Description | -| -------- | ----- | ------------ | -------- | --------------------- | -| `id` | path | string(uuid) | true | Template version ID | -| `before` | query | integer | false | Before Unix timestamp | -| `after` | query | integer | false | After Unix timestamp | -| `follow` | query | boolean | false | Follow log stream | - -### Example responses - -> 200 Response - -```json -[ - { - "created_at": "2019-08-24T14:15:22Z", - "id": 0, - "log_level": "trace", - "log_source": "string", - "output": "string", - "stage": "string" - } -] -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.ProvisionerJobLog](schemas.md#codersdkprovisionerjoblog) | - -

Response Schema

- -Status Code **200** - -| Name | Type | Required | Restrictions | Description | -| -------------- | ----------------- | -------- | ------------ | ----------- | -| `[array item]` | array | false | | | -| `» created_at` | string(date-time) | false | | | -| `» id` | integer | false | | | -| `» log_level` | string | false | | | -| `» log_source` | string | false | | | -| `» output` | string | false | | | -| `» stage` | string | false | | | - -#### Enumerated Values - -| Property | Value | -| ----------- | ------- | -| `log_level` | `trace` | -| `log_level` | `debug` | -| `log_level` | `info` | -| `log_level` | `warn` | -| `log_level` | `error` | - -To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. - -## Get template version schema by ID - -### Code samples - -```shell -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/templateversions/{id}/schema \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /templateversions/{id}/schema` - -### Parameters - -| Name | In | Type | Required | Description | -| ---- | ---- | ------------ | -------- | ------------------- | -| `id` | path | string(uuid) | true | Template version ID | - -### Example responses - -> 200 Response - -```json -[ - { - "created_at": "string", - "default_source_value": true, - "destination_scheme": "string", - "id": "string", - "name": "string", - "schema_id": "string", - "scope": "string", - "scope_id": "string", - "source_scheme": "string", - "source_value": "string", - "updated_at": "string" - } -] -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [parameter.ComputedValue](schemas.md#parametercomputedvalue) | - -

Response Schema

- -Status Code **200** - -| Name | Type | Required | Restrictions | Description | -| ------------------------ | ------- | -------- | ------------ | ----------- | -| `[array item]` | array | false | | | -| `» created_at` | string | false | | | -| `» default_source_value` | boolean | false | | | -| `» destination_scheme` | string | false | | | -| `» id` | string | false | | | -| `» name` | string | false | | | -| `» schema_id` | string | false | | | -| `» scope` | string | false | | | -| `» scope_id` | string | false | | | -| `» source_scheme` | string | false | | | -| `» source_value` | string | false | | | -| `» updated_at` | string | false | | | - -To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. - ## Get template version dry-run by job ID ### Code samples @@ -1667,3 +1603,415 @@ Status Code **200** | `workspace_transition` | `delete` | To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get logs by template version + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/logs \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templateversions/{templateversion}/logs` + +### Parameters + +| Name | In | Type | Required | Description | +| ----------------- | ----- | ------------ | -------- | --------------------- | +| `templateversion` | path | string(uuid) | true | Template version ID | +| `before` | query | integer | false | Before Unix timestamp | +| `after` | query | integer | false | After Unix timestamp | +| `follow` | query | boolean | false | Follow log stream | + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "2019-08-24T14:15:22Z", + "id": 0, + "log_level": "trace", + "log_source": "string", + "output": "string", + "stage": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.ProvisionerJobLog](schemas.md#codersdkprovisionerjoblog) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| -------------- | ----------------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | false | | | +| `» id` | integer | false | | | +| `» log_level` | string | false | | | +| `» log_source` | string | false | | | +| `» output` | string | false | | | +| `» stage` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| ----------- | ------- | +| `log_level` | `trace` | +| `log_level` | `debug` | +| `log_level` | `info` | +| `log_level` | `warn` | +| `log_level` | `error` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get parameters by template version + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/parameters \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templateversions/{templateversion}/parameters` + +### Parameters + +| Name | In | Type | Required | Description | +| ----------------- | ---- | ------------ | -------- | ------------------- | +| `templateversion` | path | string(uuid) | true | Template version ID | + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "string", + "default_source_value": true, + "destination_scheme": "string", + "id": "string", + "name": "string", + "schema_id": "string", + "scope": "string", + "scope_id": "string", + "source_scheme": "string", + "source_value": "string", + "updated_at": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [parameter.ComputedValue](schemas.md#parametercomputedvalue) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| ------------------------ | ------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string | false | | | +| `» default_source_value` | boolean | false | | | +| `» destination_scheme` | string | false | | | +| `» id` | string | false | | | +| `» name` | string | false | | | +| `» schema_id` | string | false | | | +| `» scope` | string | false | | | +| `» scope_id` | string | false | | | +| `» source_scheme` | string | false | | | +| `» source_value` | string | false | | | +| `» updated_at` | string | false | | | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get resources by template version + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/resources \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templateversions/{templateversion}/resources` + +### Parameters + +| Name | In | Type | Required | Description | +| ----------------- | ---- | ------------ | -------- | ------------------- | +| `templateversion` | path | string(uuid) | true | Template version ID | + +### Example responses + +> 200 Response + +```json +[ + { + "agents": [ + { + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "architecture": "string", + "connection_timeout_seconds": 0, + "created_at": "2019-08-24T14:15:22Z", + "directory": "string", + "disconnected_at": "2019-08-24T14:15:22Z", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "instance_id": "string", + "last_connected_at": "2019-08-24T14:15:22Z", + "latency": { + "property1": { + "latency_ms": 0, + "preferred": true + }, + "property2": { + "latency_ms": 0, + "preferred": true + } + }, + "name": "string", + "operating_system": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", + "startup_script": "string", + "status": "connecting", + "troubleshooting_url": "string", + "updated_at": "2019-08-24T14:15:22Z", + "version": "string" + } + ], + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "hide": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "metadata": [ + { + "key": "string", + "sensitive": true, + "value": "string" + } + ], + "name": "string", + "type": "string", + "workspace_transition": "start" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.WorkspaceResource](schemas.md#codersdkworkspaceresource) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | ---------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | DisplayName is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on
the client or not. | +| `»»» health` | string | false | | | +| `»»» healthcheck` | `codersdk.Healthcheck` | false | | | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies
an icon to be displayed in the dashboard. | +| `»»» id` | string(uuid) | false | | | +| `»»» sharing_level` | string | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the
`coder server` or via a hostname-based dev URL. If this is set to true
and there is no app wildcard configured on the server, the app will not
be accessible in the UI. | +| `»»» url` | string | false | | URL is the address being proxied to inside the workspace.
If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string(date-time) | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» first_connected_at` | string(date-time) | false | | | +| `»» id` | string(uuid) | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string(date-time) | false | | | +| `»» latency` | object | false | | DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | `codersdk.DERPRegion` | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» resource_id` | string(uuid) | false | | | +| `»» startup_script` | string | false | | | +| `»» status` | string | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» version` | string | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| ---------------------- | --------------- | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Get schema by template version + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/schema \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templateversions/{templateversion}/schema` + +### Parameters + +| Name | In | Type | Required | Description | +| ----------------- | ---- | ------------ | -------- | ------------------- | +| `templateversion` | path | string(uuid) | true | Template version ID | + +### Example responses + +> 200 Response + +```json +[ + { + "allow_override_destination": true, + "allow_override_source": true, + "created_at": "2019-08-24T14:15:22Z", + "default_destination_scheme": "none", + "default_refresh": "string", + "default_source_scheme": "none", + "default_source_value": "string", + "description": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "name": "string", + "redisplay_value": true, + "validation_condition": "string", + "validation_contains": ["string"], + "validation_error": "string", + "validation_type_system": "string", + "validation_value_type": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.ParameterSchema](schemas.md#codersdkparameterschema) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| ------------------------------ | ----------------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» allow_override_destination` | boolean | false | | | +| `» allow_override_source` | boolean | false | | | +| `» created_at` | string(date-time) | false | | | +| `» default_destination_scheme` | string | false | | | +| `» default_refresh` | string | false | | | +| `» default_source_scheme` | string | false | | | +| `» default_source_value` | string | false | | | +| `» description` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | +| `» name` | string | false | | | +| `» redisplay_value` | boolean | false | | | +| `» validation_condition` | string | false | | | +| `» validation_contains` | array | false | | This is a special array of items provided if the validation condition
explicitly states the value must be one of a set. | +| `» validation_error` | string | false | | | +| `» validation_type_system` | string | false | | | +| `» validation_value_type` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| ---------------------------- | ---------------------- | +| `default_destination_scheme` | `none` | +| `default_destination_scheme` | `environment_variable` | +| `default_destination_scheme` | `provisioner_variable` | +| `default_source_scheme` | `none` | +| `default_source_scheme` | `data` | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. From 5c96bd523d166951747615a9d0cc3a88151417e2 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 4 Jan 2023 16:29:07 +0100 Subject: [PATCH 21/61] Fix --- coderd/apidoc/docs.go | 28 ++++++------- coderd/apidoc/swagger.json | 28 ++++++------- coderd/templateversions.go | 28 ++++++------- docs/api/templates.md | 84 +++++++++++++++++++------------------- 4 files changed, 84 insertions(+), 84 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 2429df3b8232a..b65d9e7da6b66 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1300,7 +1300,7 @@ const docTemplate = `{ } } }, - "/templateversions/{id}": { + "/templateversions/{templateversion}": { "get": { "security": [ { @@ -1320,7 +1320,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "id", + "name": "templateversion", "in": "path", "required": true } @@ -1335,7 +1335,7 @@ const docTemplate = `{ } } }, - "/templateversions/{id}/cancel": { + "/templateversions/{templateversion}/cancel": { "patch": { "security": [ { @@ -1355,7 +1355,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "id", + "name": "templateversion", "in": "path", "required": true } @@ -1370,7 +1370,7 @@ const docTemplate = `{ } } }, - "/templateversions/{id}/dry-run": { + "/templateversions/{templateversion}/dry-run": { "post": { "security": [ { @@ -1393,7 +1393,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "id", + "name": "templateversion", "in": "path", "required": true }, @@ -1417,7 +1417,7 @@ const docTemplate = `{ } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}": { + "/templateversions/{templateversion}/dry-run/{jobid}": { "get": { "security": [ { @@ -1440,7 +1440,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "templateversionid", + "name": "templateversion", "in": "path", "required": true }, @@ -1463,7 +1463,7 @@ const docTemplate = `{ } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}/cancel": { + "/templateversions/{templateversion}/dry-run/{jobid}/cancel": { "patch": { "security": [ { @@ -1483,7 +1483,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "id", + "name": "templateversion", "in": "path", "required": true } @@ -1498,7 +1498,7 @@ const docTemplate = `{ } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}/logs": { + "/templateversions/{templateversion}/dry-run/{jobid}/logs": { "get": { "security": [ { @@ -1518,7 +1518,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "templateversionid", + "name": "templateversion", "in": "path", "required": true }, @@ -1562,7 +1562,7 @@ const docTemplate = `{ } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}/resources": { + "/templateversions/{templateversion}/dry-run/{jobid}/resources": { "get": { "security": [ { @@ -1582,7 +1582,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "templateversionid", + "name": "templateversion", "in": "path", "required": true }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index d600ce601784a..e92fef8adfbd0 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1136,7 +1136,7 @@ } } }, - "/templateversions/{id}": { + "/templateversions/{templateversion}": { "get": { "security": [ { @@ -1152,7 +1152,7 @@ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "id", + "name": "templateversion", "in": "path", "required": true } @@ -1167,7 +1167,7 @@ } } }, - "/templateversions/{id}/cancel": { + "/templateversions/{templateversion}/cancel": { "patch": { "security": [ { @@ -1183,7 +1183,7 @@ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "id", + "name": "templateversion", "in": "path", "required": true } @@ -1198,7 +1198,7 @@ } } }, - "/templateversions/{id}/dry-run": { + "/templateversions/{templateversion}/dry-run": { "post": { "security": [ { @@ -1215,7 +1215,7 @@ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "id", + "name": "templateversion", "in": "path", "required": true }, @@ -1239,7 +1239,7 @@ } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}": { + "/templateversions/{templateversion}/dry-run/{jobid}": { "get": { "security": [ { @@ -1256,7 +1256,7 @@ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "templateversionid", + "name": "templateversion", "in": "path", "required": true }, @@ -1279,7 +1279,7 @@ } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}/cancel": { + "/templateversions/{templateversion}/dry-run/{jobid}/cancel": { "patch": { "security": [ { @@ -1295,7 +1295,7 @@ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "id", + "name": "templateversion", "in": "path", "required": true } @@ -1310,7 +1310,7 @@ } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}/logs": { + "/templateversions/{templateversion}/dry-run/{jobid}/logs": { "get": { "security": [ { @@ -1326,7 +1326,7 @@ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "templateversionid", + "name": "templateversion", "in": "path", "required": true }, @@ -1370,7 +1370,7 @@ } } }, - "/templateversions/{templateversionid}/dry-run/{jobid}/resources": { + "/templateversions/{templateversion}/dry-run/{jobid}/resources": { "get": { "security": [ { @@ -1386,7 +1386,7 @@ "type": "string", "format": "uuid", "description": "Template version ID", - "name": "templateversionid", + "name": "templateversion", "in": "path", "required": true }, diff --git a/coderd/templateversions.go b/coderd/templateversions.go index e606d170a6601..46c0973cbb47a 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -33,9 +33,9 @@ import ( // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template version ID" format(uuid) +// @Param templateversion path string true "Template version ID" format(uuid) // @Success 200 {object} codersdk.TemplateVersion -// @Router /templateversions/{id} [get] +// @Router /templateversions/{templateversion} [get] func (api *API) templateVersion(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var ( @@ -74,9 +74,9 @@ func (api *API) templateVersion(rw http.ResponseWriter, r *http.Request) { // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template version ID" format(uuid) +// @Param templateversion path string true "Template version ID" format(uuid) // @Success 200 {object} codersdk.Response -// @Router /templateversions/{id}/cancel [patch] +// @Router /templateversions/{templateversion}/cancel [patch] func (api *API) patchCancelTemplateVersion(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var ( @@ -251,10 +251,10 @@ func (api *API) templateVersionParameters(rw http.ResponseWriter, r *http.Reques // @Accept json // @Produce json // @Tags Templates -// @Param id path string true "Template version ID" format(uuid) +// @Param templateversion path string true "Template version ID" format(uuid) // @Param request body codersdk.CreateTemplateVersionDryRunRequest true "Dry-run request" // @Success 201 {object} codersdk.ProvisionerJob -// @Router /templateversions/{id}/dry-run [post] +// @Router /templateversions/{templateversion}/dry-run [post] func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var ( @@ -356,10 +356,10 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques // @Accept json // @Produce json // @Tags Templates -// @Param templateversionid path string true "Template version ID" format(uuid) +// @Param templateversion path string true "Template version ID" format(uuid) // @Param jobid path string true "Job ID" format(uuid) // @Success 200 {object} codersdk.ProvisionerJob -// @Router /templateversions/{templateversionid}/dry-run/{jobid} [get] +// @Router /templateversions/{templateversion}/dry-run/{jobid} [get] func (api *API) templateVersionDryRun(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() job, ok := api.fetchTemplateVersionDryRunJob(rw, r) @@ -375,10 +375,10 @@ func (api *API) templateVersionDryRun(rw http.ResponseWriter, r *http.Request) { // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param templateversionid path string true "Template version ID" format(uuid) +// @Param templateversion path string true "Template version ID" format(uuid) // @Param jobid path string true "Job ID" format(uuid) // @Success 200 {array} codersdk.WorkspaceResource -// @Router /templateversions/{templateversionid}/dry-run/{jobid}/resources [get] +// @Router /templateversions/{templateversion}/dry-run/{jobid}/resources [get] func (api *API) templateVersionDryRunResources(rw http.ResponseWriter, r *http.Request) { job, ok := api.fetchTemplateVersionDryRunJob(rw, r) if !ok { @@ -393,13 +393,13 @@ func (api *API) templateVersionDryRunResources(rw http.ResponseWriter, r *http.R // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param templateversionid path string true "Template version ID" format(uuid) +// @Param templateversion path string true "Template version ID" format(uuid) // @Param jobid path string true "Job ID" format(uuid) // @Param before query int false "Before Unix timestamp" // @Param after query int false "After Unix timestamp" // @Param follow query bool false "Follow log stream" // @Success 200 {array} codersdk.ProvisionerJobLog -// @Router /templateversions/{templateversionid}/dry-run/{jobid}/logs [get] +// @Router /templateversions/{templateversion}/dry-run/{jobid}/logs [get] func (api *API) templateVersionDryRunLogs(rw http.ResponseWriter, r *http.Request) { job, ok := api.fetchTemplateVersionDryRunJob(rw, r) if !ok { @@ -414,9 +414,9 @@ func (api *API) templateVersionDryRunLogs(rw http.ResponseWriter, r *http.Reques // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template version ID" format(uuid) +// @Param templateversion path string true "Template version ID" format(uuid) // @Success 200 {object} codersdk.Response -// @Router /templateversions/{templateversionid}/dry-run/{jobid}/cancel [patch] +// @Router /templateversions/{templateversion}/dry-run/{jobid}/cancel [patch] func (api *API) patchTemplateVersionDryRunCancel(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() templateVersion := httpmw.TemplateVersionParam(r) diff --git a/docs/api/templates.md b/docs/api/templates.md index e7c586d459b90..73c616e652e30 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -1074,18 +1074,18 @@ To perform this operation, you must be authenticated by means of one of the foll ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/templateversions/{id} \ +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /templateversions/{id}` +`GET /templateversions/{templateversion}` ### Parameters -| Name | In | Type | Required | Description | -| ---- | ---- | ------------ | -------- | ------------------- | -| `id` | path | string(uuid) | true | Template version ID | +| Name | In | Type | Required | Description | +| ----------------- | ---- | ------------ | -------- | ------------------- | +| `templateversion` | path | string(uuid) | true | Template version ID | ### Example responses @@ -1148,18 +1148,18 @@ To perform this operation, you must be authenticated by means of one of the foll ```shell # Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/templateversions/{id}/cancel \ +curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversion}/cancel \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PATCH /templateversions/{id}/cancel` +`PATCH /templateversions/{templateversion}/cancel` ### Parameters -| Name | In | Type | Required | Description | -| ---- | ---- | ------------ | -------- | ------------------- | -| `id` | path | string(uuid) | true | Template version ID | +| Name | In | Type | Required | Description | +| ----------------- | ---- | ------------ | -------- | ------------------- | +| `templateversion` | path | string(uuid) | true | Template version ID | ### Example responses @@ -1192,13 +1192,13 @@ To perform this operation, you must be authenticated by means of one of the foll ```shell # Example request using curl -curl -X POST http://coder-server:8080/api/v2/templateversions/{id}/dry-run \ +curl -X POST http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /templateversions/{id}/dry-run` +`POST /templateversions/{templateversion}/dry-run` > Body parameter @@ -1219,10 +1219,10 @@ curl -X POST http://coder-server:8080/api/v2/templateversions/{id}/dry-run \ ### Parameters -| Name | In | Type | Required | Description | -| ------ | ---- | ---------------------------------------------------------------------------------------------------- | -------- | ------------------- | -| `id` | path | string(uuid) | true | Template version ID | -| `body` | body | [codersdk.CreateTemplateVersionDryRunRequest](schemas.md#codersdkcreatetemplateversiondryrunrequest) | true | Dry-run request | +| Name | In | Type | Required | Description | +| ----------------- | ---- | ---------------------------------------------------------------------------------------------------- | -------- | ------------------- | +| `templateversion` | path | string(uuid) | true | Template version ID | +| `body` | body | [codersdk.CreateTemplateVersionDryRunRequest](schemas.md#codersdkcreatetemplateversiondryrunrequest) | true | Dry-run request | ### Example responses @@ -1260,19 +1260,19 @@ To perform this operation, you must be authenticated by means of one of the foll ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversionid}/dry-run/{jobid} \ +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run/{jobid} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /templateversions/{templateversionid}/dry-run/{jobid}` +`GET /templateversions/{templateversion}/dry-run/{jobid}` ### Parameters -| Name | In | Type | Required | Description | -| ------------------- | ---- | ------------ | -------- | ------------------- | -| `templateversionid` | path | string(uuid) | true | Template version ID | -| `jobid` | path | string(uuid) | true | Job ID | +| Name | In | Type | Required | Description | +| ----------------- | ---- | ------------ | -------- | ------------------- | +| `templateversion` | path | string(uuid) | true | Template version ID | +| `jobid` | path | string(uuid) | true | Job ID | ### Example responses @@ -1310,18 +1310,18 @@ To perform this operation, you must be authenticated by means of one of the foll ```shell # Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversionid}/dry-run/{jobid}/cancel \ +curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run/{jobid}/cancel \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PATCH /templateversions/{templateversionid}/dry-run/{jobid}/cancel` +`PATCH /templateversions/{templateversion}/dry-run/{jobid}/cancel` ### Parameters -| Name | In | Type | Required | Description | -| ---- | ---- | ------------ | -------- | ------------------- | -| `id` | path | string(uuid) | true | Template version ID | +| Name | In | Type | Required | Description | +| ----------------- | ---- | ------------ | -------- | ------------------- | +| `templateversion` | path | string(uuid) | true | Template version ID | ### Example responses @@ -1354,22 +1354,22 @@ To perform this operation, you must be authenticated by means of one of the foll ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversionid}/dry-run/{jobid}/logs \ +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run/{jobid}/logs \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /templateversions/{templateversionid}/dry-run/{jobid}/logs` +`GET /templateversions/{templateversion}/dry-run/{jobid}/logs` ### Parameters -| Name | In | Type | Required | Description | -| ------------------- | ----- | ------------ | -------- | --------------------- | -| `templateversionid` | path | string(uuid) | true | Template version ID | -| `jobid` | path | string(uuid) | true | Job ID | -| `before` | query | integer | false | Before Unix timestamp | -| `after` | query | integer | false | After Unix timestamp | -| `follow` | query | boolean | false | Follow log stream | +| Name | In | Type | Required | Description | +| ----------------- | ----- | ------------ | -------- | --------------------- | +| `templateversion` | path | string(uuid) | true | Template version ID | +| `jobid` | path | string(uuid) | true | Job ID | +| `before` | query | integer | false | Before Unix timestamp | +| `after` | query | integer | false | After Unix timestamp | +| `follow` | query | boolean | false | Follow log stream | ### Example responses @@ -1426,19 +1426,19 @@ To perform this operation, you must be authenticated by means of one of the foll ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversionid}/dry-run/{jobid}/resources \ +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run/{jobid}/resources \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /templateversions/{templateversionid}/dry-run/{jobid}/resources` +`GET /templateversions/{templateversion}/dry-run/{jobid}/resources` ### Parameters -| Name | In | Type | Required | Description | -| ------------------- | ---- | ------------ | -------- | ------------------- | -| `templateversionid` | path | string(uuid) | true | Template version ID | -| `jobid` | path | string(uuid) | true | Job ID | +| Name | In | Type | Required | Description | +| ----------------- | ---- | ------------ | -------- | ------------------- | +| `templateversion` | path | string(uuid) | true | Template version ID | +| `jobid` | path | string(uuid) | true | Job ID | ### Example responses From 4667f0761841c52d6daf864e9e1477009ab03b64 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 5 Jan 2023 14:14:56 +0100 Subject: [PATCH 22/61] WIP --- coderd/apidoc/docs.go | 242 +++++++++++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 207 +++++++++++++++++++++++++++++++ coderd/userauth.go | 21 ++++ coderd/users.go | 27 +++++ docs/api/authentication.md | 50 +++++++- docs/api/schemas.md | 84 +++++++++++++ docs/api/users.md | 164 +++++++++++++++++++++++++ docs/manifest.json | 8 ++ 8 files changed, 801 insertions(+), 2 deletions(-) create mode 100644 docs/api/users.md diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index b65d9e7da6b66..70949df688e74 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1798,6 +1798,173 @@ const docTemplate = `{ } } }, + "/users/authmethods": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get authentication methods", + "operationId": "get-authentication-methods", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AuthMethods" + } + } + } + } + }, + "/users/first": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Check initial user created", + "operationId": "check-initial-user-created", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Create initial user", + "operationId": "create-initial-user", + "parameters": [ + { + "description": "First user request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateFirstUserRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.CreateFirstUserResponse" + } + } + } + } + }, + "/users/login": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authentication" + ], + "summary": "Log in user", + "operationId": "log-in-user", + "parameters": [ + { + "description": "Login request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.LoginWithPasswordRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.LoginWithPasswordResponse" + } + } + } + } + }, + "/users/oauth2/github/callback": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "OAuth 2.0 GitHub Callback", + "operationId": "oauth2-github-callback", + "responses": { + "307": { + "description": "Temporary Redirect" + } + } + } + }, + "/users/oidc/callback": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "OpenID Connect Callback", + "operationId": "oidc-callback", + "responses": { + "307": { + "description": "Temporary Redirect" + } + } + } + }, "/users/{user}/workspace/{workspacename}": { "get": { "security": [ @@ -2995,6 +3162,20 @@ const docTemplate = `{ } } }, + "codersdk.AuthMethods": { + "type": "object", + "properties": { + "github": { + "type": "boolean" + }, + "oidc": { + "type": "boolean" + }, + "password": { + "type": "boolean" + } + } + }, "codersdk.AuthorizationCheck": { "description": "AuthorizationCheck is used to check if the currently authenticated user (or the specified user) can do a given action to a given set of objects.", "type": "object", @@ -3082,6 +3263,41 @@ const docTemplate = `{ } } }, + "codersdk.CreateFirstUserRequest": { + "type": "object", + "required": [ + "email", + "password", + "username" + ], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "trial": { + "type": "boolean" + }, + "username": { + "type": "string" + } + } + }, + "codersdk.CreateFirstUserResponse": { + "type": "object", + "properties": { + "organization_id": { + "type": "string", + "format": "uuid" + }, + "user_id": { + "type": "string", + "format": "uuid" + } + } + }, "codersdk.CreateOrganizationRequest": { "type": "object", "required": [ @@ -3708,6 +3924,32 @@ const docTemplate = `{ } } }, + "codersdk.LoginWithPasswordRequest": { + "type": "object", + "required": [ + "email", + "password" + ], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "codersdk.LoginWithPasswordResponse": { + "type": "object", + "required": [ + "session_token" + ], + "properties": { + "session_token": { + "type": "string" + } + } + }, "codersdk.OAuth2Config": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index e92fef8adfbd0..380634bbc689e 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1582,6 +1582,147 @@ } } }, + "/users/authmethods": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get authentication methods", + "operationId": "get-authentication-methods", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AuthMethods" + } + } + } + } + }, + "/users/first": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Check initial user created", + "operationId": "check-initial-user-created", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Create initial user", + "operationId": "create-initial-user", + "parameters": [ + { + "description": "First user request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateFirstUserRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.CreateFirstUserResponse" + } + } + } + } + }, + "/users/login": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Authentication"], + "summary": "Log in user", + "operationId": "log-in-user", + "parameters": [ + { + "description": "Login request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.LoginWithPasswordRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.LoginWithPasswordResponse" + } + } + } + } + }, + "/users/oauth2/github/callback": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "OAuth 2.0 GitHub Callback", + "operationId": "oauth2-github-callback", + "responses": { + "307": { + "description": "Temporary Redirect" + } + } + } + }, + "/users/oidc/callback": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "OpenID Connect Callback", + "operationId": "oidc-callback", + "responses": { + "307": { + "description": "Temporary Redirect" + } + } + } + }, "/users/{user}/workspace/{workspacename}": { "get": { "security": [ @@ -2657,6 +2798,20 @@ } } }, + "codersdk.AuthMethods": { + "type": "object", + "properties": { + "github": { + "type": "boolean" + }, + "oidc": { + "type": "boolean" + }, + "password": { + "type": "boolean" + } + } + }, "codersdk.AuthorizationCheck": { "description": "AuthorizationCheck is used to check if the currently authenticated user (or the specified user) can do a given action to a given set of objects.", "type": "object", @@ -2736,6 +2891,37 @@ } } }, + "codersdk.CreateFirstUserRequest": { + "type": "object", + "required": ["email", "password", "username"], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "trial": { + "type": "boolean" + }, + "username": { + "type": "string" + } + } + }, + "codersdk.CreateFirstUserResponse": { + "type": "object", + "properties": { + "organization_id": { + "type": "string", + "format": "uuid" + }, + "user_id": { + "type": "string", + "format": "uuid" + } + } + }, "codersdk.CreateOrganizationRequest": { "type": "object", "required": ["name"], @@ -3335,6 +3521,27 @@ } } }, + "codersdk.LoginWithPasswordRequest": { + "type": "object", + "required": ["email", "password"], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "codersdk.LoginWithPasswordResponse": { + "type": "object", + "required": ["session_token"], + "properties": { + "session_token": { + "type": "string" + } + } + }, "codersdk.OAuth2Config": { "type": "object", "properties": { diff --git a/coderd/userauth.go b/coderd/userauth.go index 1197aa8d2cea6..dfd72e7c9a9c0 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -43,6 +43,13 @@ type GithubOAuth2Config struct { AllowTeams []GithubOAuth2Team } +// @Summary Get authentication methods +// @ID get-authentication-methods +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Success 200 {object} codersdk.AuthMethods +// @Router /users/authmethods [get] func (api *API) userAuthMethods(rw http.ResponseWriter, r *http.Request) { httpapi.Write(r.Context(), rw, http.StatusOK, codersdk.AuthMethods{ Password: true, @@ -51,6 +58,13 @@ func (api *API) userAuthMethods(rw http.ResponseWriter, r *http.Request) { }) } +// @Summary OAuth 2.0 GitHub Callback +// @ID oauth2-github-callback +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Success 307 +// @Router /users/oauth2/github/callback [get] func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -200,6 +214,13 @@ type OIDCConfig struct { IgnoreEmailVerified bool } +// @Summary OpenID Connect Callback +// @ID oidc-callback +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Success 307 +// @Router /users/oidc/callback [get] func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/coderd/users.go b/coderd/users.go index ea47cd66d4553..e307f28a750ee 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -30,6 +30,14 @@ import ( "github.com/coder/coder/examples" ) +// @Summary Check initial user created +// @ID check-initial-user-created +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Success 200 {object} codersdk.Response +// @Router /users/first [get] +// // Returns whether the initial user has been created or not. func (api *API) firstUser(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -54,6 +62,16 @@ func (api *API) firstUser(rw http.ResponseWriter, r *http.Request) { }) } +// @Summary Create initial user +// @ID create-initial-user +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Users +// @Param request body codersdk.CreateFirstUserRequest true "First user request" +// @Success 201 {object} codersdk.CreateFirstUserResponse +// @Router /users/first [post] +// // Creates the initial user for a Coder deployment. func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -908,6 +926,15 @@ func (api *API) organizationByUserAndName(rw http.ResponseWriter, r *http.Reques httpapi.Write(ctx, rw, http.StatusOK, convertOrganization(organization)) } +// @Summary Log in user +// @ID log-in-user +// @Security CoderSessionToken +// @Produce json +// @Tags Authentication +// @Param request body codersdk.LoginWithPasswordRequest true "Login request" +// @Success 201 {object} codersdk.LoginWithPasswordResponse +// @Router /users/login [post] +// // Authenticates the user with an email and password. func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/docs/api/authentication.md b/docs/api/authentication.md index 8c3c209b1ae7c..b323a93c685e2 100644 --- a/docs/api/authentication.md +++ b/docs/api/authentication.md @@ -1,4 +1,50 @@ # Authentication -- API Key (CoderSessionToken) - - Parameter Name: **Coder-Session-Token**, in: header. +> This page is incomplete, stay tuned. + +## Log in user + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/users/login \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /users/login` + +> Body parameter + +```json +{ + "email": "string", + "password": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | -------------------------------------------------------------------------------- | -------- | ------------- | +| `body` | body | [codersdk.LoginWithPasswordRequest](schemas.md#codersdkloginwithpasswordrequest) | true | Login request | + +### Example responses + +> 201 Response + +```json +{ + "session_token": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------ | ----------- | ---------------------------------------------------------------------------------- | +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.LoginWithPasswordResponse](schemas.md#codersdkloginwithpasswordresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 87cffd52a8d78..085628179616a 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -286,6 +286,24 @@ | `audit_logs` | array of [codersdk.AuditLog](#codersdkauditlog) | false | | | | `count` | integer | false | | | +## codersdk.AuthMethods + +```json +{ + "github": true, + "oidc": true, + "password": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------- | ------- | -------- | ------------ | ----------- | +| `github` | boolean | false | | | +| `oidc` | boolean | false | | | +| `password` | boolean | false | | | + ## codersdk.AuthorizationCheck ```json @@ -421,6 +439,42 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `external_url` | string | false | | External URL references the current Coder version. For production builds, this will link directly to a release. For development builds, this will link to a commit. | | `version` | string | false | | Version returns the semantic version of the build. | +## codersdk.CreateFirstUserRequest + +```json +{ + "email": "string", + "password": "string", + "trial": true, + "username": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------- | ------- | -------- | ------------ | ----------- | +| `email` | string | true | | | +| `password` | string | true | | | +| `trial` | boolean | false | | | +| `username` | string | true | | | + +## codersdk.CreateFirstUserResponse + +```json +{ + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------------- | ------ | -------- | ------------ | ----------- | +| `organization_id` | string | false | | | +| `user_id` | string | false | | | + ## codersdk.CreateOrganizationRequest ```json @@ -2021,6 +2075,36 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | | `url` | string | false | | URL specifies the endpoint to check for the app health. | +## codersdk.LoginWithPasswordRequest + +```json +{ + "email": "string", + "password": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------- | ------ | -------- | ------------ | ----------- | +| `email` | string | true | | | +| `password` | string | true | | | + +## codersdk.LoginWithPasswordResponse + +```json +{ + "session_token": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| --------------- | ------ | -------- | ------------ | ----------- | +| `session_token` | string | true | | | + ## codersdk.OAuth2Config ```json diff --git a/docs/api/users.md b/docs/api/users.md new file mode 100644 index 0000000000000..a8609c9157560 --- /dev/null +++ b/docs/api/users.md @@ -0,0 +1,164 @@ +# Users + +> This page is incomplete, stay tuned. + +## Get authentication methods + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/authmethods \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/authmethods` + +### Example responses + +> 200 Response + +```json +{ + "github": true, + "oidc": true, + "password": true +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.AuthMethods](schemas.md#codersdkauthmethods) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Check initial user created + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/first \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/first` + +### Example responses + +> 200 Response + +```json +{ + "detail": "string", + "message": "string", + "validations": [ + { + "detail": "string", + "field": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## Create initial user + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/users/first \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /users/first` + +> Body parameter + +```json +{ + "email": "string", + "password": "string", + "trial": true, + "username": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ---------------------------------------------------------------------------- | -------- | ------------------ | +| `body` | body | [codersdk.CreateFirstUserRequest](schemas.md#codersdkcreatefirstuserrequest) | true | First user request | + +### Example responses + +> 201 Response + +```json +{ + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------ | ----------- | ------------------------------------------------------------------------------ | +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.CreateFirstUserResponse](schemas.md#codersdkcreatefirstuserresponse) | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## OAuth 2.0 GitHub Callback + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/oauth2/github/callback \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/oauth2/github/callback` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ----------------------------------------------------------------------- | ------------------ | ------ | +| 307 | [Temporary Redirect](https://tools.ietf.org/html/rfc7231#section-6.4.7) | Temporary Redirect | | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. + +## OpenID Connect Callback + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/oidc/callback \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/oidc/callback` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ----------------------------------------------------------------------- | ------------------ | ------ | +| 307 | [Temporary Redirect](https://tools.ietf.org/html/rfc7231#section-6.4.7) | Temporary Redirect | | + +To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**. diff --git a/docs/manifest.json b/docs/manifest.json index 603771bedcb0b..22546066c1dee 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -369,6 +369,14 @@ "title": "Parameters", "path": "./api/parameters.md" }, + { + "title": "Users", + "path": "./api/users.md" + }, + { + "title": "Authentication", + "path": "./api/authentication.md" + }, { "title": "Agents", "path": "./api/agents.md" From 5405c0cc861056112a861e9bd02116454197cb00 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 10:27:48 +0100 Subject: [PATCH 23/61] WIP --- coderd/apidoc/docs.go | 9 ++++++--- coderd/apidoc/swagger.json | 9 ++++++--- codersdk/templates.go | 6 +++--- docs/api/schemas.md | 6 +++--- docs/api/templates.md | 36 ++++++++++++++++++------------------ 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 75f0fc8d2a3dd..9981be1f869ff 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -4192,7 +4192,8 @@ const docTemplate = `{ "type": "integer" }, "active_version_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "allow_user_cancel_workspace_jobs": { "type": "boolean" @@ -4273,7 +4274,8 @@ const docTemplate = `{ "type": "string" }, "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "markdown": { "type": "string" @@ -4363,7 +4365,8 @@ const docTemplate = `{ ], "properties": { "id": { - "type": "string" + "type": "string", + "format": "uuid" } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 6c299f672d80a..1e5e21d62b630 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -3788,7 +3788,8 @@ "type": "integer" }, "active_version_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "allow_user_cancel_workspace_jobs": { "type": "boolean" @@ -3869,7 +3870,8 @@ "type": "string" }, "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "markdown": { "type": "string" @@ -3957,7 +3959,8 @@ "required": ["id"], "properties": { "id": { - "type": "string" + "type": "string", + "format": "uuid" } } }, diff --git a/codersdk/templates.go b/codersdk/templates.go index ee14775430005..2985f6e3aac15 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -21,7 +21,7 @@ type Template struct { Name string `json:"name"` DisplayName string `json:"display_name"` Provisioner ProvisionerType `json:"provisioner"` - ActiveVersionID uuid.UUID `json:"active_version_id"` + ActiveVersionID uuid.UUID `json:"active_version_id" format:"uuid"` WorkspaceOwnerCount uint32 `json:"workspace_owner_count"` // ActiveUserCount is set to -1 when loading. ActiveUserCount int `json:"active_user_count"` @@ -42,7 +42,7 @@ type TransitionStats struct { type TemplateBuildTimeStats map[WorkspaceTransition]TransitionStats type UpdateActiveTemplateVersion struct { - ID uuid.UUID `json:"id" validate:"required"` + ID uuid.UUID `json:"id" validate:"required" format:"uuid"` } type TemplateRole string @@ -83,7 +83,7 @@ type UpdateTemplateMeta struct { } type TemplateExample struct { - ID string `json:"id"` + ID string `json:"id" format:"uuid"` URL string `json:"url"` Name string `json:"name"` Description string `json:"description"` diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 95c599564570e..d7c417a343460 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2955,7 +2955,7 @@ Parameter represents a set value for the scope. ```json { "active_user_count": 0, - "active_version_id": "string", + "active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc", "allow_user_cancel_workspace_jobs": true, "build_time_stats": { "property1": { @@ -3051,7 +3051,7 @@ Parameter represents a set value for the scope. { "description": "string", "icon": "string", - "id": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "markdown": "string", "name": "string", "tags": ["string"], @@ -3198,7 +3198,7 @@ Parameter represents a set value for the scope. ```json { - "id": "string" + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08" } ``` diff --git a/docs/api/templates.md b/docs/api/templates.md index 6cbe43073fb25..81c4aa5024213 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -29,7 +29,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat [ { "active_user_count": 0, - "active_version_id": "string", + "active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc", "allow_user_cancel_workspace_jobs": true, "build_time_stats": { "property1": { @@ -72,7 +72,7 @@ Status Code **200** | ------------------------------------ | --------------------------------- | -------- | ------------ | ------------------------------------------ | | `[array item]` | array | false | | | | `» active_user_count` | integer | false | | ActiveUserCount is set to -1 when loading. | -| `» active_version_id` | string | false | | | +| `» active_version_id` | string(uuid) | false | | | | `» allow_user_cancel_workspace_jobs` | boolean | false | | | | `» build_time_stats` | `codersdk.TemplateBuildTimeStats` | false | | | | `»» [any property]` | `codersdk.TransitionStats` | false | | | @@ -145,7 +145,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa ```json { "active_user_count": 0, - "active_version_id": "string", + "active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc", "allow_user_cancel_workspace_jobs": true, "build_time_stats": { "property1": { @@ -209,7 +209,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat { "description": "string", "icon": "string", - "id": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "markdown": "string", "name": "string", "tags": ["string"], @@ -228,16 +228,16 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat Status Code **200** -| Name | Type | Required | Restrictions | Description | -| --------------- | ------ | -------- | ------------ | ----------- | -| `[array item]` | array | false | | | -| `» description` | string | false | | | -| `» icon` | string | false | | | -| `» id` | string | false | | | -| `» markdown` | string | false | | | -| `» name` | string | false | | | -| `» tags` | array | false | | | -| `» url` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| --------------- | ------------ | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» description` | string | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» markdown` | string | false | | | +| `» name` | string | false | | | +| `» tags` | array | false | | | +| `» url` | string | false | | | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -268,7 +268,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat ```json { "active_user_count": 0, - "active_version_id": "string", + "active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc", "allow_user_cancel_workspace_jobs": true, "build_time_stats": { "property1": { @@ -573,7 +573,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{id} \ ```json { "active_user_count": 0, - "active_version_id": "string", + "active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc", "allow_user_cancel_workspace_jobs": true, "build_time_stats": { "property1": { @@ -679,7 +679,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{id} \ ```json { "active_user_count": 0, - "active_version_id": "string", + "active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc", "allow_user_cancel_workspace_jobs": true, "build_time_stats": { "property1": { @@ -903,7 +903,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{id}/versions \ ```json { - "id": "string" + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08" } ``` From 0dc1d2ab216a4806c192c11ff536e09db28aeee6 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 10:45:21 +0100 Subject: [PATCH 24/61] Users --- coderd/apidoc/docs.go | 150 ++++++++++++++++++++++++++++++++++++- coderd/apidoc/swagger.json | 135 ++++++++++++++++++++++++++++++++- coderd/templateversions.go | 3 + coderd/users.go | 21 ++++++ codersdk/users.go | 4 +- docs/api/audit.md | 2 +- docs/api/schemas.md | 61 ++++++++++++++- docs/api/templates.md | 25 ++++--- docs/api/users.md | 120 +++++++++++++++++++++++++++++ 9 files changed, 501 insertions(+), 20 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 05aca50e2d4d2..58eb9c2acc55c 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1195,6 +1195,25 @@ const docTemplate = `{ "name": "id", "in": "path", "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "After ID", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" } ], "responses": { @@ -1798,6 +1817,95 @@ const docTemplate = `{ } } }, + "/users": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get users", + "operationId": "get-users", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "q", + "in": "query" + }, + { + "type": "string", + "format": "uuid", + "description": "After ID", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.GetUsersResponse" + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Create new user", + "operationId": "create-new-user", + "parameters": [ + { + "description": "Create user request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateUserRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, "/users/authmethods": { "get": { "security": [ @@ -3439,6 +3547,31 @@ const docTemplate = `{ } } }, + "codersdk.CreateUserRequest": { + "type": "object", + "required": [ + "email", + "organization_id", + "password", + "username" + ], + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "organization_id": { + "type": "string", + "format": "uuid" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "codersdk.CreateWorkspaceBuildRequest": { "type": "object", "required": [ @@ -3858,6 +3991,20 @@ const docTemplate = `{ } } }, + "codersdk.GetUsersResponse": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "users": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.User" + } + } + } + }, "codersdk.GitAuthConfig": { "type": "object", "properties": { @@ -4691,7 +4838,8 @@ const docTemplate = `{ "format": "date-time" }, "email": { - "type": "string" + "type": "string", + "format": "email" }, "id": { "type": "string", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 52fc33a6af492..064b1758280d7 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1041,6 +1041,25 @@ "name": "id", "in": "path", "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "After ID", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" } ], "responses": { @@ -1582,6 +1601,85 @@ } } }, + "/users": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get users", + "operationId": "get-users", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "q", + "in": "query" + }, + { + "type": "string", + "format": "uuid", + "description": "After ID", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.GetUsersResponse" + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Create new user", + "operationId": "create-new-user", + "parameters": [ + { + "description": "Create user request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateUserRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, "/users/authmethods": { "get": { "security": [ @@ -3045,6 +3143,26 @@ } } }, + "codersdk.CreateUserRequest": { + "type": "object", + "required": ["email", "organization_id", "password", "username"], + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "organization_id": { + "type": "string", + "format": "uuid" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "codersdk.CreateWorkspaceBuildRequest": { "type": "object", "required": ["transition"], @@ -3457,6 +3575,20 @@ } } }, + "codersdk.GetUsersResponse": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "users": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.User" + } + } + } + }, "codersdk.GitAuthConfig": { "type": "object", "properties": { @@ -4245,7 +4377,8 @@ "format": "date-time" }, "email": { - "type": "string" + "type": "string", + "format": "email" }, "id": { "type": "string", diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 46c0973cbb47a..15ac9a502711b 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -541,6 +541,9 @@ func (api *API) fetchTemplateVersionDryRunJob(rw http.ResponseWriter, r *http.Re // @Produce json // @Tags Templates // @Param id path string true "Template ID" format(uuid) +// @Param after_id query string false "After ID" format(uuid) +// @Param limit query int false "Page limit" +// @Param offset query int false "Page offset" // @Success 200 {array} codersdk.TemplateVersion // @Router /templates/{id}/versions [get] func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Request) { diff --git a/coderd/users.go b/coderd/users.go index 1a1ad8ea05449..7076cc119ae32 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -153,6 +153,17 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) { }) } +// @Summary Get users +// @ID get-users +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param q query string false "Search query" +// @Param after_id query string false "After ID" format(uuid) +// @Param limit query int false "Page limit" +// @Param offset query int false "Page offset" +// @Success 200 {object} codersdk.GetUsersResponse +// @Router /users [get] func (api *API) users(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() query := r.URL.Query().Get("q") @@ -232,6 +243,16 @@ func (api *API) users(rw http.ResponseWriter, r *http.Request) { } // Creates a new user. +// +// @Summary Create new user +// @ID create-new-user +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Users +// @Param request body codersdk.CreateUserRequest true "Create user request" +// @Success 201 {object} codersdk.User +// @Router /users [post] func (api *API) postUser(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() auditor := *api.Auditor.Load() diff --git a/codersdk/users.go b/codersdk/users.go index 4b1f24afac933..b6f2926c09a0b 100644 --- a/codersdk/users.go +++ b/codersdk/users.go @@ -37,7 +37,7 @@ type UsersRequest struct { type User struct { ID uuid.UUID `json:"id" validate:"required" table:"id" format:"uuid"` Username string `json:"username" validate:"required" table:"username"` - Email string `json:"email" validate:"required" table:"email"` + Email string `json:"email" validate:"required" table:"email" format:"email"` CreatedAt time.Time `json:"created_at" validate:"required" table:"created at" format:"date-time"` LastSeenAt time.Time `json:"last_seen_at" format:"date-time"` @@ -66,7 +66,7 @@ type CreateFirstUserResponse struct { } type CreateUserRequest struct { - Email string `json:"email" validate:"required,email"` + Email string `json:"email" validate:"required,email" format:"email"` Username string `json:"username" validate:"required,username"` Password string `json:"password" validate:"required"` OrganizationID uuid.UUID `json:"organization_id" validate:"required" format:"uuid"` diff --git a/docs/api/audit.md b/docs/api/audit.md index edaeca1e0dfe8..4c94f8eb815a8 100644 --- a/docs/api/audit.md +++ b/docs/api/audit.md @@ -62,7 +62,7 @@ curl -X GET http://coder-server:8080/api/v2/audit?q=string \ "user": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", - "email": "string", + "email": "user@example.com", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "last_seen_at": "2019-08-24T14:15:22Z", "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], diff --git a/docs/api/schemas.md b/docs/api/schemas.md index a236dc23ce88e..7d7328e9cdcc9 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -183,7 +183,7 @@ "user": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", - "email": "string", + "email": "user@example.com", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "last_seen_at": "2019-08-24T14:15:22Z", "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], @@ -259,7 +259,7 @@ "user": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", - "email": "string", + "email": "user@example.com", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "last_seen_at": "2019-08-24T14:15:22Z", "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], @@ -623,6 +623,26 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `resource_type` | `api_key` | | `resource_type` | `group` | +## codersdk.CreateUserRequest + +```json +{ + "email": "user@example.com", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "password": "string", + "username": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------------- | ------ | -------- | ------------ | ----------- | +| `email` | string | true | | | +| `organization_id` | string | true | | | +| `password` | string | true | | | +| `username` | string | true | | | + ## codersdk.CreateWorkspaceBuildRequest ```json @@ -2012,6 +2032,39 @@ CreateParameterRequest is a structure used to create a new parameter value for a | ------ | ------ | -------- | ------------ | ------------------------------------------------------------- | | `host` | string | false | | Host is the externally accessible URL for the Coder instance. | +## codersdk.GetUsersResponse + +```json +{ + "count": 0, + "users": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + } + ] +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------- | --------------------------------------- | -------- | ------------ | ----------- | +| `count` | integer | false | | | +| `users` | array of [codersdk.User](#codersdkuser) | false | | | + ## codersdk.GitAuthConfig ```json @@ -3163,7 +3216,7 @@ Parameter represents a set value for the scope. "created_by": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", - "email": "string", + "email": "user@example.com", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "last_seen_at": "2019-08-24T14:15:22Z", "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], @@ -3386,7 +3439,7 @@ Parameter represents a set value for the scope. { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", - "email": "string", + "email": "user@example.com", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "last_seen_at": "2019-08-24T14:15:22Z", "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], diff --git a/docs/api/templates.md b/docs/api/templates.md index 81c4aa5024213..749836cc8789a 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -352,7 +352,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "created_by": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", - "email": "string", + "email": "user@example.com", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "last_seen_at": "2019-08-24T14:15:22Z", "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], @@ -427,7 +427,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "created_by": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", - "email": "string", + "email": "user@example.com", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "last_seen_at": "2019-08-24T14:15:22Z", "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], @@ -502,7 +502,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "created_by": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", - "email": "string", + "email": "user@example.com", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "last_seen_at": "2019-08-24T14:15:22Z", "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], @@ -772,9 +772,12 @@ curl -X GET http://coder-server:8080/api/v2/templates/{id}/versions \ ### Parameters -| Name | In | Type | Required | Description | -| ---- | ---- | ------------ | -------- | ----------- | -| `id` | path | string(uuid) | true | Template ID | +| Name | In | Type | Required | Description | +| ---------- | ----- | ------------ | -------- | ----------- | +| `id` | path | string(uuid) | true | Template ID | +| `after_id` | query | string(uuid) | false | After ID | +| `limit` | query | integer | false | Page limit | +| `offset` | query | integer | false | Page offset | ### Example responses @@ -787,7 +790,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{id}/versions \ "created_by": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", - "email": "string", + "email": "user@example.com", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "last_seen_at": "2019-08-24T14:15:22Z", "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], @@ -842,7 +845,7 @@ Status Code **200** | `» created_by` | `codersdk.User` | false | | | | `»» avatar_url` | string(uri) | false | | | | `»» created_at` | string(date-time) | true | | | -| `»» email` | string | true | | | +| `»» email` | string(email) | true | | | | `»» id` | string(uuid) | true | | | | `»» last_seen_at` | string(date-time) | false | | | | `»» organization_ids` | array | false | | | @@ -970,7 +973,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{id}/versions/{templatever "created_by": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", - "email": "string", + "email": "user@example.com", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "last_seen_at": "2019-08-24T14:15:22Z", "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], @@ -1025,7 +1028,7 @@ Status Code **200** | `» created_by` | `codersdk.User` | false | | | | `»» avatar_url` | string(uri) | false | | | | `»» created_at` | string(date-time) | true | | | -| `»» email` | string | true | | | +| `»» email` | string(email) | true | | | | `»» id` | string(uuid) | true | | | | `»» last_seen_at` | string(date-time) | false | | | | `»» organization_ids` | array | false | | | @@ -1097,7 +1100,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion} \ "created_by": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", - "email": "string", + "email": "user@example.com", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "last_seen_at": "2019-08-24T14:15:22Z", "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], diff --git a/docs/api/users.md b/docs/api/users.md index 14c4678cfa48d..30d2ec3eb2421 100644 --- a/docs/api/users.md +++ b/docs/api/users.md @@ -2,6 +2,126 @@ > This page is incomplete, stay tuned. +## Get users + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------- | ----- | ------------ | -------- | ------------ | +| `q` | query | string | false | Search query | +| `after_id` | query | string(uuid) | false | After ID | +| `limit` | query | integer | false | Page limit | +| `offset` | query | integer | false | Page offset | + +### Example responses + +> 200 Response + +```json +{ + "count": 0, + "users": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.GetUsersResponse](schemas.md#codersdkgetusersresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Create new user + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/users \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /users` + +> Body parameter + +```json +{ + "email": "user@example.com", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "password": "string", + "username": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------------------------------------------------------------------ | -------- | ------------------- | +| `body` | body | [codersdk.CreateUserRequest](schemas.md#codersdkcreateuserrequest) | true | Create user request | + +### Example responses + +> 201 Response + +```json +{ + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------ | ----------- | ---------------------------------------- | +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.User](schemas.md#codersdkuser) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Get authentication methods ### Code samples From 928542560f56a60be96e263cb4dc48f873ba6ca0 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 10:57:51 +0100 Subject: [PATCH 25/61] Logout --- coderd/apidoc/docs.go | 53 ++++++++++++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 45 ++++++++++++++++++++++++++++++++ coderd/roles.go | 12 +++++++-- coderd/users.go | 8 ++++++ docs/api/members.md | 46 +++++++++++++++++++++++++++++++++ docs/api/users.md | 38 +++++++++++++++++++++++++++ 6 files changed, 200 insertions(+), 2 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 58eb9c2acc55c..53961afea9561 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2029,6 +2029,31 @@ const docTemplate = `{ } } }, + "/users/logout": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Log out user", + "operationId": "log-out-user", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, "/users/oauth2/github/callback": { "get": { "security": [ @@ -2073,6 +2098,34 @@ const docTemplate = `{ } } }, + "/users/roles": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Members" + ], + "summary": "Get site member roles", + "operationId": "get-site-member-roles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.AssignableRoles" + } + } + } + } + } + }, "/users/{user}/workspace/{workspacename}": { "get": { "security": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 064b1758280d7..60c66f9bf6d11 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1785,6 +1785,27 @@ } } }, + "/users/logout": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Log out user", + "operationId": "log-out-user", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, "/users/oauth2/github/callback": { "get": { "security": [ @@ -1821,6 +1842,30 @@ } } }, + "/users/roles": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Members"], + "summary": "Get site member roles", + "operationId": "get-site-member-roles", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.AssignableRoles" + } + } + } + } + } + }, "/users/{user}/workspace/{workspacename}": { "get": { "security": [ diff --git a/coderd/roles.go b/coderd/roles.go index 9eb1518d9ec6e..29be9c4a49172 100644 --- a/coderd/roles.go +++ b/coderd/roles.go @@ -11,6 +11,14 @@ import ( ) // assignableSiteRoles returns all site wide roles that can be assigned. +// +// @Summary Get site member roles +// @ID get-site-member-roles +// @Security CoderSessionToken +// @Produce json +// @Tags Members +// @Success 200 {array} codersdk.AssignableRoles +// @Router /users/roles [get] func (api *API) assignableSiteRoles(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() actorRoles := httpmw.UserAuthorization(r) @@ -23,6 +31,8 @@ func (api *API) assignableSiteRoles(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, assignableRoles(actorRoles.Roles, roles)) } +// assignableSiteRoles returns all org wide roles that can be assigned. +// // @Summary Get member roles by organization // @ID get-member-roles-by-organization // @Security CoderSessionToken @@ -31,8 +41,6 @@ func (api *API) assignableSiteRoles(rw http.ResponseWriter, r *http.Request) { // @Param organization path string true "Organization ID" format(uuid) // @Success 200 {array} codersdk.AssignableRoles // @Router /organizations/{organization}/members/roles [get] -// -// assignableSiteRoles returns all site wide roles that can be assigned. func (api *API) assignableOrgRoles(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organization := httpmw.OrganizationParam(r) diff --git a/coderd/users.go b/coderd/users.go index 7076cc119ae32..69da8a0e53e14 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -970,6 +970,14 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) { } // Clear the user's session cookie. +// +// @Summary Log out user +// @ID log-out-user +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Success 200 {object} codersdk.Response +// @Router /users/logout [post] func (api *API) postLogout(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() // Get a blank token cookie. diff --git a/docs/api/members.md b/docs/api/members.md index 389c6dcdba0e9..4b6d47095de6b 100644 --- a/docs/api/members.md +++ b/docs/api/members.md @@ -110,3 +110,49 @@ curl -X PUT http://coder-server:8080/api/v2/organizations/{organization}/members | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.OrganizationMember](schemas.md#codersdkorganizationmember) | To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get site member roles + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/roles \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/roles` + +### Example responses + +> 200 Response + +```json +[ + { + "assignable": true, + "display_name": "string", + "name": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.AssignableRoles](schemas.md#codersdkassignableroles) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| ---------------- | ------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» assignable` | boolean | false | | | +| `» display_name` | string | false | | | +| `» name` | string | false | | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/docs/api/users.md b/docs/api/users.md index 30d2ec3eb2421..7674388c5deeb 100644 --- a/docs/api/users.md +++ b/docs/api/users.md @@ -243,6 +243,44 @@ curl -X POST http://coder-server:8080/api/v2/users/first \ To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Log out user + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/users/logout \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /users/logout` + +### Example responses + +> 200 Response + +```json +{ + "detail": "string", + "message": "string", + "validations": [ + { + "detail": "string", + "field": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## OAuth 2.0 GitHub Callback ### Code samples From 6979a7f483e1acf64cdddb5e3b540ca571adf82d Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 11:08:17 +0100 Subject: [PATCH 26/61] User profile --- coderd/apidoc/docs.go | 88 +++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 78 ++++++++++++++++++++++++++ coderd/users.go | 26 +++++++++ docs/api/schemas.md | 14 +++++ docs/api/users.md | 110 +++++++++++++++++++++++++++++++++++++ 5 files changed, 316 insertions(+) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 53961afea9561..5a6c94007b821 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2126,6 +2126,83 @@ const docTemplate = `{ } } }, + "/users/{user}": { + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user by name", + "operationId": "get-user-by-name", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, + "/users/{user}/profile": { + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Update user profile", + "operationId": "update-user-profile", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "Updated profile", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserProfileRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, "/users/{user}/workspace/{workspacename}": { "get": { "security": [ @@ -4840,6 +4917,17 @@ const docTemplate = `{ } } }, + "codersdk.UpdateUserProfileRequest": { + "type": "object", + "required": [ + "username" + ], + "properties": { + "username": { + "type": "string" + } + } + }, "codersdk.UpdateWorkspaceAutostartRequest": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 60c66f9bf6d11..585b1c8095442 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1866,6 +1866,75 @@ } } }, + "/users/{user}": { + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get user by name", + "operationId": "get-user-by-name", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, + "/users/{user}/profile": { + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Update user profile", + "operationId": "update-user-profile", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "Updated profile", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserProfileRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, "/users/{user}/workspace/{workspacename}": { "get": { "security": [ @@ -4376,6 +4445,15 @@ } } }, + "codersdk.UpdateUserProfileRequest": { + "type": "object", + "required": ["username"], + "properties": { + "username": { + "type": "string" + } + } + }, "codersdk.UpdateWorkspaceAutostartRequest": { "type": "object", "properties": { diff --git a/coderd/users.go b/coderd/users.go index 69da8a0e53e14..07da139d26253 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -339,6 +339,14 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusCreated, convertUser(user, []uuid.UUID{req.OrganizationID})) } +// @Summary Delete user +// @ID delete-user +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Success 200 {object} codersdk.User +// @Router /users/{user} [delete] func (api *API) deleteUser(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() auditor := *api.Auditor.Load() @@ -394,6 +402,15 @@ func (api *API) deleteUser(rw http.ResponseWriter, r *http.Request) { // Returns the parameterized user requested. All validation // is completed in the middleware for this route. +// +// @Summary Get user by name +// @ID get-user-by-name +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Success 200 {object} codersdk.User +// @Router /users/{user} [delete] func (api *API) userByName(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() user := httpmw.UserParam(r) @@ -415,6 +432,15 @@ func (api *API) userByName(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, convertUser(user, organizationIDs)) } +// @Summary Update user profile +// @ID update-user-profile +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Param request body codersdk.UpdateUserProfileRequest true "Updated profile" +// @Success 200 {object} codersdk.User +// @Router /users/{user}/profile [put] func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 7d7328e9cdcc9..ce5ab4c256317 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -3377,6 +3377,20 @@ Parameter represents a set value for the scope. | ------- | --------------- | -------- | ------------ | ----------- | | `roles` | array of string | false | | | +## codersdk.UpdateUserProfileRequest + +```json +{ + "username": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------- | ------ | -------- | ------------ | ----------- | +| `username` | string | true | | | + ## codersdk.UpdateWorkspaceAutostartRequest ```json diff --git a/docs/api/users.md b/docs/api/users.md index 7674388c5deeb..c2b9ab9711f02 100644 --- a/docs/api/users.md +++ b/docs/api/users.md @@ -320,3 +320,113 @@ curl -X GET http://coder-server:8080/api/v2/users/oidc/callback \ | 307 | [Temporary Redirect](https://tools.ietf.org/html/rfc7231#section-6.4.7) | Temporary Redirect | | To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get user by name + +### Code samples + +```shell +# Example request using curl +curl -X DELETE http://coder-server:8080/api/v2/users/{user} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`DELETE /users/{user}` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.User](schemas.md#codersdkuser) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update user profile + +### Code samples + +```shell +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/users/{user}/profile \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /users/{user}/profile` + +> Body parameter + +```json +{ + "username": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | -------------------------------------------------------------------------------- | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | +| `body` | body | [codersdk.UpdateUserProfileRequest](schemas.md#codersdkupdateuserprofilerequest) | true | Updated profile | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.User](schemas.md#codersdkuser) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). From 62a26f91df36bbfb146037372ac2da629de7bd39 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 11:28:47 +0100 Subject: [PATCH 27/61] Status suspend activate --- coderd/apidoc/docs.go | 68 +++++++++++++++++++++++++ coderd/apidoc/swagger.json | 60 ++++++++++++++++++++++ coderd/coderd.go | 4 +- coderd/users.go | 24 +++++++++ docs/api/users.md | 100 +++++++++++++++++++++++++++++++++++++ 5 files changed, 254 insertions(+), 2 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 5a6c94007b821..3760b4972eaeb 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2203,6 +2203,74 @@ const docTemplate = `{ } } }, + "/users/{user}/status/activate": { + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Activate user account", + "operationId": "activate-user-account", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, + "/users/{user}/status/suspend": { + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Suspend user account", + "operationId": "suspend-user-account", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, "/users/{user}/workspace/{workspacename}": { "get": { "security": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 585b1c8095442..85de35c654d58 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1935,6 +1935,66 @@ } } }, + "/users/{user}/status/activate": { + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Activate user account", + "operationId": "activate-user-account", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, + "/users/{user}/status/suspend": { + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Suspend user account", + "operationId": "suspend-user-account", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, "/users/{user}/workspace/{workspacename}": { "get": { "security": [ diff --git a/coderd/coderd.go b/coderd/coderd.go index 37db9d44e448c..1c647c3bf28f8 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -462,8 +462,8 @@ func New(options *Options) *API { r.Get("/", api.userByName) r.Put("/profile", api.putUserProfile) r.Route("/status", func(r chi.Router) { - r.Put("/suspend", api.putUserStatus(database.UserStatusSuspended)) - r.Put("/activate", api.putUserStatus(database.UserStatusActive)) + r.Put("/suspend", api.putSuspendUserAccount()) + r.Put("/activate", api.putActivateUserAccount()) }) r.Route("/password", func(r chi.Router) { r.Put("/", api.putUserPassword) diff --git a/coderd/users.go b/coderd/users.go index 07da139d26253..510fad34aab70 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -521,6 +521,30 @@ func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, convertUser(updatedUserProfile, organizationIDs)) } +// @Summary Suspend user account +// @ID suspend-user-account +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Success 200 {object} codersdk.User +// @Router /users/{user}/status/suspend [put] +func (api *API) putSuspendUserAccount() func(rw http.ResponseWriter, r *http.Request) { + return api.putUserStatus(database.UserStatusSuspended) +} + +// @Summary Activate user account +// @ID activate-user-account +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Success 200 {object} codersdk.User +// @Router /users/{user}/status/activate [put] +func (api *API) putActivateUserAccount() func(rw http.ResponseWriter, r *http.Request) { + return api.putUserStatus(database.UserStatusSuspended) +} + func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseWriter, r *http.Request) { return func(rw http.ResponseWriter, r *http.Request) { var ( diff --git a/docs/api/users.md b/docs/api/users.md index c2b9ab9711f02..2c07230967b20 100644 --- a/docs/api/users.md +++ b/docs/api/users.md @@ -430,3 +430,103 @@ curl -X PUT http://coder-server:8080/api/v2/users/{user}/profile \ | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.User](schemas.md#codersdkuser) | To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Activate user account + +### Code samples + +```shell +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/users/{user}/status/activate \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /users/{user}/status/activate` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.User](schemas.md#codersdkuser) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Suspend user account + +### Code samples + +```shell +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/users/{user}/status/suspend \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /users/{user}/status/suspend` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.User](schemas.md#codersdkuser) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). From b1f389babde24f25c857b6bdf96e2e52f36e2c01 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 11:36:16 +0100 Subject: [PATCH 28/61] User roles --- coderd/apidoc/docs.go | 190 ++++++++++++++++++++++++++++++++--- coderd/apidoc/swagger.json | 170 +++++++++++++++++++++++++++++--- coderd/members.go | 4 +- coderd/users.go | 29 +++++- docs/api/members.md | 10 +- docs/api/schemas.md | 16 +++ docs/api/users.md | 197 +++++++++++++++++++++++++++++++++++++ 7 files changed, 582 insertions(+), 34 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 3760b4972eaeb..7f2729b15b9b2 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -507,13 +507,11 @@ const docTemplate = `{ "operationId": "assign-role-to-organization-member", "parameters": [ { - "description": "Update roles request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateRoles" - } + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true }, { "type": "string", @@ -523,11 +521,13 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "Username, UUID, or me", - "name": "user", - "in": "path", - "required": true + "description": "Update roles request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateRoles" + } } ], "responses": { @@ -2127,7 +2127,7 @@ const docTemplate = `{ } }, "/users/{user}": { - "delete": { + "get": { "security": [ { "CoderSessionToken": [] @@ -2158,6 +2158,78 @@ const docTemplate = `{ } } } + }, + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Delete user", + "operationId": "delete-user", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, + "/users/{user}/password": { + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Update user password", + "operationId": "update-user-password", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "Update password request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserPasswordRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } } }, "/users/{user}/profile": { @@ -2203,6 +2275,84 @@ const docTemplate = `{ } } }, + "/users/{user}/roles": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user roles", + "operationId": "get-user-roles", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + }, + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Assign role to user", + "operationId": "assign-role-to-user", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "Update roles request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateRoles" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, "/users/{user}/status/activate": { "put": { "security": [ @@ -4985,6 +5135,20 @@ const docTemplate = `{ } } }, + "codersdk.UpdateUserPasswordRequest": { + "type": "object", + "required": [ + "password" + ], + "properties": { + "old_password": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, "codersdk.UpdateUserProfileRequest": { "type": "object", "required": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 85de35c654d58..027509059fdb6 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -435,13 +435,11 @@ "operationId": "assign-role-to-organization-member", "parameters": [ { - "description": "Update roles request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateRoles" - } + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true }, { "type": "string", @@ -451,11 +449,13 @@ "required": true }, { - "type": "string", - "description": "Username, UUID, or me", - "name": "user", - "in": "path", - "required": true + "description": "Update roles request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateRoles" + } } ], "responses": { @@ -1867,7 +1867,7 @@ } }, "/users/{user}": { - "delete": { + "get": { "security": [ { "CoderSessionToken": [] @@ -1894,6 +1894,70 @@ } } } + }, + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Delete user", + "operationId": "delete-user", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, + "/users/{user}/password": { + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Update user password", + "operationId": "update-user-password", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "Update password request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserPasswordRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } } }, "/users/{user}/profile": { @@ -1935,6 +1999,74 @@ } } }, + "/users/{user}/roles": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get user roles", + "operationId": "get-user-roles", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + }, + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Assign role to user", + "operationId": "assign-role-to-user", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "Update roles request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateRoles" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, "/users/{user}/status/activate": { "put": { "security": [ @@ -4505,6 +4637,18 @@ } } }, + "codersdk.UpdateUserPasswordRequest": { + "type": "object", + "required": ["password"], + "properties": { + "old_password": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, "codersdk.UpdateUserProfileRequest": { "type": "object", "required": ["username"], diff --git a/coderd/members.go b/coderd/members.go index 9141cadb6a0b3..0cba45b723639 100644 --- a/coderd/members.go +++ b/coderd/members.go @@ -22,9 +22,9 @@ import ( // @Accept json // @Produce json // @Tags Members -// @Param request body codersdk.UpdateRoles true "Update roles request" +// @Param user path string true "User ID, name, or me" // @Param organization path string true "Organization ID" -// @Param user path string true "Username, UUID, or me" +// @Param request body codersdk.UpdateRoles true "Update roles request" // @Success 200 {object} codersdk.OrganizationMember // @Router /organizations/{organization}/members/{user}/roles [put] func (api *API) putMemberRoles(rw http.ResponseWriter, r *http.Request) { diff --git a/coderd/users.go b/coderd/users.go index 510fad34aab70..093681c55ceb5 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -410,7 +410,7 @@ func (api *API) deleteUser(rw http.ResponseWriter, r *http.Request) { // @Tags Users // @Param user path string true "User ID, name, or me" // @Success 200 {object} codersdk.User -// @Router /users/{user} [delete] +// @Router /users/{user} [get] func (api *API) userByName(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() user := httpmw.UserParam(r) @@ -614,6 +614,15 @@ func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseW } } +// @Summary Update user password +// @ID update-user-password +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Param request body codersdk.UpdateUserPasswordRequest true "Update password request" +// @Success 204 +// @Router /users/{user}/password [put] func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -731,6 +740,14 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusNoContent, nil) } +// @Summary Get user roles +// @ID get-user-roles +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Success 200 {object} codersdk.User +// @Router /users/{user}/roles [get] func (api *API) userRoles(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() user := httpmw.UserParam(r) @@ -774,6 +791,16 @@ func (api *API) userRoles(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, resp) } +// @Summary Assign role to user +// @ID assign-role-to-user +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Param request body codersdk.UpdateRoles true "Update roles request" +// @Success 200 {object} codersdk.User +// @Router /users/{user}/roles [put] func (api *API) putUserRoles(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/docs/api/members.md b/docs/api/members.md index 4b6d47095de6b..21eea27021292 100644 --- a/docs/api/members.md +++ b/docs/api/members.md @@ -78,11 +78,11 @@ curl -X PUT http://coder-server:8080/api/v2/organizations/{organization}/members ### Parameters -| Name | In | Type | Required | Description | -| -------------- | ---- | ------------------------------------------------------ | -------- | --------------------- | -| `organization` | path | string | true | Organization ID | -| `user` | path | string | true | Username, UUID, or me | -| `body` | body | [codersdk.UpdateRoles](schemas.md#codersdkupdateroles) | true | Update roles request | +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------------------------------------------------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | +| `organization` | path | string | true | Organization ID | +| `body` | body | [codersdk.UpdateRoles](schemas.md#codersdkupdateroles) | true | Update roles request | ### Example responses diff --git a/docs/api/schemas.md b/docs/api/schemas.md index ce5ab4c256317..1018c7d41981b 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -3377,6 +3377,22 @@ Parameter represents a set value for the scope. | ------- | --------------- | -------- | ------------ | ----------- | | `roles` | array of string | false | | | +## codersdk.UpdateUserPasswordRequest + +```json +{ + "old_password": "string", + "password": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------------- | ------ | -------- | ------------ | ----------- | +| `old_password` | string | false | | | +| `password` | string | true | | | + ## codersdk.UpdateUserProfileRequest ```json diff --git a/docs/api/users.md b/docs/api/users.md index 2c07230967b20..33fb7fd02a1c7 100644 --- a/docs/api/users.md +++ b/docs/api/users.md @@ -325,6 +325,56 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ### Code samples +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/{user} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/{user}` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.User](schemas.md#codersdkuser) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Delete user + +### Code samples + ```shell # Example request using curl curl -X DELETE http://coder-server:8080/api/v2/users/{user} \ @@ -371,6 +421,43 @@ curl -X DELETE http://coder-server:8080/api/v2/users/{user} \ To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Update user password + +### Code samples + +```shell +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/users/{user}/password \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /users/{user}/password` + +> Body parameter + +```json +{ + "old_password": "string", + "password": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ---------------------------------------------------------------------------------- | -------- | ----------------------- | +| `user` | path | string | true | User ID, name, or me | +| `body` | body | [codersdk.UpdateUserPasswordRequest](schemas.md#codersdkupdateuserpasswordrequest) | true | Update password request | + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | --------------------------------------------------------------- | ----------- | ------ | +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Update user profile ### Code samples @@ -431,6 +518,116 @@ curl -X PUT http://coder-server:8080/api/v2/users/{user}/profile \ To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Get user roles + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/{user}/roles \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/{user}/roles` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.User](schemas.md#codersdkuser) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Assign role to user + +### Code samples + +```shell +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/users/{user}/roles \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /users/{user}/roles` + +> Body parameter + +```json +{ + "roles": ["string"] +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------------------------------------------------------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | +| `body` | body | [codersdk.UpdateRoles](schemas.md#codersdkupdateroles) | true | Update roles request | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.User](schemas.md#codersdkuser) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Activate user account ### Code samples From 14a1654983d7160bbc7977c32591a0b535a2f1ab Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 11:48:44 +0100 Subject: [PATCH 29/61] User tokens --- coderd/apidoc/docs.go | 194 +++++++++++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 171 ++++++++++++++++++++++++++++++++ coderd/apikey.go | 27 ++++++ codersdk/apikey.go | 16 +-- docs/api/schemas.md | 78 +++++++++++++++ docs/api/users.md | 160 ++++++++++++++++++++++++++++++ 6 files changed, 638 insertions(+), 8 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 7f2729b15b9b2..cc9210f9be314 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2192,6 +2192,118 @@ const docTemplate = `{ } } }, + "/users/{user}/keys": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Create new session key", + "operationId": "create-new-session-key", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.GenerateAPIKeyResponse" + } + } + } + } + }, + "/users/{user}/keys/tokens": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user tokens", + "operationId": "get-user-tokens", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.APIKey" + } + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Create token API key", + "operationId": "create-token-api-key", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "Create token request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTokenRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.GenerateAPIKeyResponse" + } + } + } + } + }, "/users/{user}/password": { "put": { "security": [ @@ -3444,6 +3556,65 @@ const docTemplate = `{ } } }, + "codersdk.APIKey": { + "type": "object", + "required": [ + "created_at", + "expires_at", + "id", + "last_used", + "lifetime_seconds", + "login_type", + "scope", + "updated_at", + "user_id" + ], + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "string" + }, + "last_used": { + "type": "string", + "format": "date-time" + }, + "lifetime_seconds": { + "type": "integer" + }, + "login_type": { + "type": "string", + "enum": [ + "password", + "github", + "oidc", + "token" + ] + }, + "scope": { + "type": "string", + "enum": [ + "all", + "application_connect" + ] + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "user_id": { + "description": "NOTE: do not ever return the HashedSecret", + "type": "string", + "format": "uuid" + } + } + }, "codersdk.AWSInstanceIdentityToken": { "type": "object", "required": [ @@ -3895,6 +4066,21 @@ const docTemplate = `{ } } }, + "codersdk.CreateTokenRequest": { + "type": "object", + "properties": { + "lifetime": { + "type": "integer" + }, + "scope": { + "type": "string", + "enum": [ + "all", + "application_connect" + ] + } + } + }, "codersdk.CreateUserRequest": { "type": "object", "required": [ @@ -4330,6 +4516,14 @@ const docTemplate = `{ } } }, + "codersdk.GenerateAPIKeyResponse": { + "type": "object", + "properties": { + "key": { + "type": "string" + } + } + }, "codersdk.GetAppHostResponse": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 027509059fdb6..c673cf4fe922d 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1924,6 +1924,106 @@ } } }, + "/users/{user}/keys": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Create new session key", + "operationId": "create-new-session-key", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.GenerateAPIKeyResponse" + } + } + } + } + }, + "/users/{user}/keys/tokens": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get user tokens", + "operationId": "get-user-tokens", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.APIKey" + } + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Create token API key", + "operationId": "create-token-api-key", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "Create token request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateTokenRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.GenerateAPIKeyResponse" + } + } + } + } + }, "/users/{user}/password": { "put": { "security": [ @@ -3031,6 +3131,57 @@ } } }, + "codersdk.APIKey": { + "type": "object", + "required": [ + "created_at", + "expires_at", + "id", + "last_used", + "lifetime_seconds", + "login_type", + "scope", + "updated_at", + "user_id" + ], + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "string" + }, + "last_used": { + "type": "string", + "format": "date-time" + }, + "lifetime_seconds": { + "type": "integer" + }, + "login_type": { + "type": "string", + "enum": ["password", "github", "oidc", "token"] + }, + "scope": { + "type": "string", + "enum": ["all", "application_connect"] + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "user_id": { + "description": "NOTE: do not ever return the HashedSecret", + "type": "string", + "format": "uuid" + } + } + }, "codersdk.AWSInstanceIdentityToken": { "type": "object", "required": ["document", "signature"], @@ -3449,6 +3600,18 @@ } } }, + "codersdk.CreateTokenRequest": { + "type": "object", + "properties": { + "lifetime": { + "type": "integer" + }, + "scope": { + "type": "string", + "enum": ["all", "application_connect"] + } + } + }, "codersdk.CreateUserRequest": { "type": "object", "required": ["email", "organization_id", "password", "username"], @@ -3872,6 +4035,14 @@ } } }, + "codersdk.GenerateAPIKeyResponse": { + "type": "object", + "properties": { + "key": { + "type": "string" + } + } + }, "codersdk.GetAppHostResponse": { "type": "object", "properties": { diff --git a/coderd/apikey.go b/coderd/apikey.go index 2900f8e4abe62..95c28041f63a3 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -25,6 +25,16 @@ import ( ) // Creates a new token API key that effectively doesn't expire. +// +// @Summary Create token API key +// @ID create-token-api-key +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Param request body codersdk.CreateTokenRequest true "Create token request" +// @Success 201 {object} codersdk.GenerateAPIKeyResponse +// @Router /users/{user}/keys/tokens [post] func (api *API) postToken(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() user := httpmw.UserParam(r) @@ -78,6 +88,15 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) { } // Creates a new session key, used for logging in via the CLI. +// +// @Summary Create new session key +// @ID create-new-session-key +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Success 201 {object} codersdk.GenerateAPIKeyResponse +// @Router /users/{user}/keys [post] func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() user := httpmw.UserParam(r) @@ -140,6 +159,14 @@ func (api *API) apiKey(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, convertAPIKey(key)) } +// @Summary Get user tokens +// @ID get-user-tokens +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Success 200 {array} codersdk.APIKey +// @Router /users/{user}/keys/tokens [get] func (api *API) tokens(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/codersdk/apikey.go b/codersdk/apikey.go index 8fa0180e7b2b8..fd896a0d29b4a 100644 --- a/codersdk/apikey.go +++ b/codersdk/apikey.go @@ -13,13 +13,13 @@ import ( type APIKey struct { ID string `json:"id" validate:"required"` // NOTE: do not ever return the HashedSecret - UserID uuid.UUID `json:"user_id" validate:"required"` - LastUsed time.Time `json:"last_used" validate:"required"` - ExpiresAt time.Time `json:"expires_at" validate:"required"` - CreatedAt time.Time `json:"created_at" validate:"required"` - UpdatedAt time.Time `json:"updated_at" validate:"required"` - LoginType LoginType `json:"login_type" validate:"required"` - Scope APIKeyScope `json:"scope" validate:"required"` + UserID uuid.UUID `json:"user_id" validate:"required" format:"uuid"` + LastUsed time.Time `json:"last_used" validate:"required" format:"date-time"` + ExpiresAt time.Time `json:"expires_at" validate:"required" format:"date-time"` + CreatedAt time.Time `json:"created_at" validate:"required" format:"date-time"` + UpdatedAt time.Time `json:"updated_at" validate:"required" format:"date-time"` + LoginType LoginType `json:"login_type" validate:"required" enums:"password,github,oidc,token"` + Scope APIKeyScope `json:"scope" validate:"required" enums:"all,application_connect"` LifetimeSeconds int64 `json:"lifetime_seconds" validate:"required"` } @@ -41,7 +41,7 @@ const ( type CreateTokenRequest struct { Lifetime time.Duration `json:"lifetime"` - Scope APIKeyScope `json:"scope"` + Scope APIKeyScope `json:"scope" enums:"all,application_connect"` } // GenerateAPIKeyResponse contains an API key for a user. diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 1018c7d41981b..07ba125aae45c 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -16,6 +16,47 @@ | ------------ | ------ | -------- | ------------ | ----------- | | `csp-report` | object | false | | | +## codersdk.APIKey + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "expires_at": "2019-08-24T14:15:22Z", + "id": "string", + "last_used": "2019-08-24T14:15:22Z", + "lifetime_seconds": 0, + "login_type": "password", + "scope": "all", + "updated_at": "2019-08-24T14:15:22Z", + "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | ------- | -------- | ------------ | ------------------------------------------- | +| `created_at` | string | true | | | +| `expires_at` | string | true | | | +| `id` | string | true | | | +| `last_used` | string | true | | | +| `lifetime_seconds` | integer | true | | | +| `login_type` | string | true | | | +| `scope` | string | true | | | +| `updated_at` | string | true | | | +| `user_id` | string | true | | User ID do not ever return the HashedSecret | + +#### Enumerated Values + +| Property | Value | +| ------------ | --------------------- | +| `login_type` | `password` | +| `login_type` | `github` | +| `login_type` | `oidc` | +| `login_type` | `token` | +| `scope` | `all` | +| `scope` | `application_connect` | + ## codersdk.AWSInstanceIdentityToken ```json @@ -623,6 +664,29 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `resource_type` | `api_key` | | `resource_type` | `group` | +## codersdk.CreateTokenRequest + +```json +{ + "lifetime": 0, + "scope": "all" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------- | ------- | -------- | ------------ | ----------- | +| `lifetime` | integer | false | | | +| `scope` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| -------- | --------------------- | +| `scope` | `all` | +| `scope` | `application_connect` | + ## codersdk.CreateUserRequest ```json @@ -2018,6 +2082,20 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `usage` | string | false | | | | `value` | integer | false | | | +## codersdk.GenerateAPIKeyResponse + +```json +{ + "key": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----- | ------ | -------- | ------------ | ----------- | +| `key` | string | false | | | + ## codersdk.GetAppHostResponse ```json diff --git a/docs/api/users.md b/docs/api/users.md index 33fb7fd02a1c7..5817ad064e372 100644 --- a/docs/api/users.md +++ b/docs/api/users.md @@ -421,6 +421,166 @@ curl -X DELETE http://coder-server:8080/api/v2/users/{user} \ To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Create new session key + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/users/{user}/keys \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /users/{user}/keys` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | + +### Example responses + +> 201 Response + +```json +{ + "key": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------ | ----------- | ---------------------------------------------------------------------------- | +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.GenerateAPIKeyResponse](schemas.md#codersdkgenerateapikeyresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get user tokens + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/{user}/keys/tokens \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/{user}/keys/tokens` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "2019-08-24T14:15:22Z", + "expires_at": "2019-08-24T14:15:22Z", + "id": "string", + "last_used": "2019-08-24T14:15:22Z", + "lifetime_seconds": 0, + "login_type": "password", + "scope": "all", + "updated_at": "2019-08-24T14:15:22Z", + "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.APIKey](schemas.md#codersdkapikey) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| -------------------- | ----------------- | -------- | ------------ | ----------------------------------------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | true | | | +| `» expires_at` | string(date-time) | true | | | +| `» id` | string | true | | | +| `» last_used` | string(date-time) | true | | | +| `» lifetime_seconds` | integer | true | | | +| `» login_type` | string | true | | | +| `» scope` | string | true | | | +| `» updated_at` | string(date-time) | true | | | +| `» user_id` | string(uuid) | true | | NOTE: do not ever return the HashedSecret | + +#### Enumerated Values + +| Property | Value | +| ------------ | --------------------- | +| `login_type` | `password` | +| `login_type` | `github` | +| `login_type` | `oidc` | +| `login_type` | `token` | +| `scope` | `all` | +| `scope` | `application_connect` | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Create token API key + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/users/{user}/keys/tokens \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /users/{user}/keys/tokens` + +> Body parameter + +```json +{ + "lifetime": 0, + "scope": "all" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | -------------------------------------------------------------------- | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | +| `body` | body | [codersdk.CreateTokenRequest](schemas.md#codersdkcreatetokenrequest) | true | Create token request | + +### Example responses + +> 201 Response + +```json +{ + "key": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------ | ----------- | ---------------------------------------------------------------------------- | +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.GenerateAPIKeyResponse](schemas.md#codersdkgenerateapikeyresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Update user password ### Code samples From 5c2262926f546d73b99fa167f3e15e26fa17fdda Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 11:51:07 +0100 Subject: [PATCH 30/61] Keys --- coderd/apidoc/docs.go | 79 ++++++++++++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 71 ++++++++++++++++++++++++++++++++++ coderd/apikey.go | 18 +++++++++ docs/api/users.md | 73 +++++++++++++++++++++++++++++++++++ 4 files changed, 241 insertions(+) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index cc9210f9be314..7bd07c4cd389f 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2304,6 +2304,85 @@ const docTemplate = `{ } } }, + "/users/{user}/keys/{keyid}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get API key", + "operationId": "get-user-tokens", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Key ID", + "name": "keyid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.APIKey" + } + } + } + }, + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Delete API key", + "operationId": "delete-user-tokens", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Key ID", + "name": "keyid", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, "/users/{user}/password": { "put": { "security": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index c673cf4fe922d..e55dca1a8481a 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -2024,6 +2024,77 @@ } } }, + "/users/{user}/keys/{keyid}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get API key", + "operationId": "get-user-tokens", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Key ID", + "name": "keyid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.APIKey" + } + } + } + }, + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Delete API key", + "operationId": "delete-user-tokens", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Key ID", + "name": "keyid", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, "/users/{user}/password": { "put": { "security": [ diff --git a/coderd/apikey.go b/coderd/apikey.go index 95c28041f63a3..d759794e73838 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -131,6 +131,15 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusCreated, codersdk.GenerateAPIKeyResponse{Key: cookie.Value}) } +// @Summary Get API key +// @ID get-user-tokens +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Param keyid path string true "Key ID" format(uuid) +// @Success 200 {object} codersdk.APIKey +// @Router /users/{user}/keys/{keyid} [get] func (api *API) apiKey(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -199,6 +208,15 @@ func (api *API) tokens(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, apiKeys) } +// @Summary Delete API key +// @ID delete-user-tokens +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Param keyid path string true "Key ID" format(uuid) +// @Success 204 +// @Router /users/{user}/keys/{keyid} [delete] func (api *API) deleteAPIKey(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/docs/api/users.md b/docs/api/users.md index 5817ad064e372..f1c1880ac0596 100644 --- a/docs/api/users.md +++ b/docs/api/users.md @@ -581,6 +581,79 @@ curl -X POST http://coder-server:8080/api/v2/users/{user}/keys/tokens \ To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Get API key + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/{user}/keys/{keyid} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/{user}/keys/{keyid}` + +### Parameters + +| Name | In | Type | Required | Description | +| ------- | ---- | ------------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | +| `keyid` | path | string(uuid) | true | Key ID | + +### Example responses + +> 200 Response + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "expires_at": "2019-08-24T14:15:22Z", + "id": "string", + "last_used": "2019-08-24T14:15:22Z", + "lifetime_seconds": 0, + "login_type": "password", + "scope": "all", + "updated_at": "2019-08-24T14:15:22Z", + "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.APIKey](schemas.md#codersdkapikey) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Delete API key + +### Code samples + +```shell +# Example request using curl +curl -X DELETE http://coder-server:8080/api/v2/users/{user}/keys/{keyid} \ + -H 'Coder-Session-Token: API_KEY' +``` + +`DELETE /users/{user}/keys/{keyid}` + +### Parameters + +| Name | In | Type | Required | Description | +| ------- | ---- | ------------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | +| `keyid` | path | string(uuid) | true | Key ID | + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | --------------------------------------------------------------- | ----------- | ------ | +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Update user password ### Code samples From 91c78f43ffb6396f03f82f946e167d4d13a77ce0 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 11:56:47 +0100 Subject: [PATCH 31/61] SSH key --- coderd/apidoc/docs.go | 86 ++++++++++++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 78 ++++++++++++++++++++++++++++++++++ coderd/gitsshkey.go | 16 +++++++ codersdk/gitsshkey.go | 6 +-- docs/api/schemas.md | 20 +++++++++ docs/api/users.md | 80 +++++++++++++++++++++++++++++++++++ 6 files changed, 283 insertions(+), 3 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 7bd07c4cd389f..2bbf76f8294dd 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2192,6 +2192,72 @@ const docTemplate = `{ } } }, + "/users/{user}/gitsshkey": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user Git SSH key", + "operationId": "get-user-git-ssh-key", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.GitSSHKey" + } + } + } + }, + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Regenerate user SSH key", + "operationId": "regenerate-user-ssh-key", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.GitSSHKey" + } + } + } + } + }, "/users/{user}/keys": { "post": { "security": [ @@ -4661,6 +4727,26 @@ const docTemplate = `{ } } }, + "codersdk.GitSSHKey": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "public_key": { + "type": "string" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "user_id": { + "type": "string", + "format": "uuid" + } + } + }, "codersdk.GoogleInstanceIdentityToken": { "type": "object", "required": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index e55dca1a8481a..736e74bbef4e1 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1924,6 +1924,64 @@ } } }, + "/users/{user}/gitsshkey": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get user Git SSH key", + "operationId": "get-user-git-ssh-key", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.GitSSHKey" + } + } + } + }, + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Regenerate user SSH key", + "operationId": "regenerate-user-ssh-key", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.GitSSHKey" + } + } + } + } + }, "/users/{user}/keys": { "post": { "security": [ @@ -4172,6 +4230,26 @@ } } }, + "codersdk.GitSSHKey": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "public_key": { + "type": "string" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "user_id": { + "type": "string", + "format": "uuid" + } + } + }, "codersdk.GoogleInstanceIdentityToken": { "type": "object", "required": ["json_web_token"], diff --git a/coderd/gitsshkey.go b/coderd/gitsshkey.go index a0f15da2e9e0c..9e68d7a64283d 100644 --- a/coderd/gitsshkey.go +++ b/coderd/gitsshkey.go @@ -12,6 +12,14 @@ import ( "github.com/coder/coder/codersdk" ) +// @Summary Regenerate user SSH key +// @ID regenerate-user-ssh-key +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Success 200 {object} codersdk.GitSSHKey +// @Router /users/{user}/gitsshkey [put] func (api *API) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -73,6 +81,14 @@ func (api *API) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) { }) } +// @Summary Get user Git SSH key +// @ID get-user-git-ssh-key +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Success 200 {object} codersdk.GitSSHKey +// @Router /users/{user}/gitsshkey [get] func (api *API) gitSSHKey(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() user := httpmw.UserParam(r) diff --git a/codersdk/gitsshkey.go b/codersdk/gitsshkey.go index e345a2733ab02..4a19110b5bb90 100644 --- a/codersdk/gitsshkey.go +++ b/codersdk/gitsshkey.go @@ -12,9 +12,9 @@ import ( ) type GitSSHKey struct { - UserID uuid.UUID `json:"user_id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + UserID uuid.UUID `json:"user_id" format:"uuid"` + CreatedAt time.Time `json:"created_at" format:"date-time"` + UpdatedAt time.Time `json:"updated_at" format:"date-time"` PublicKey string `json:"public_key"` } diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 07ba125aae45c..08fc7bbc13624 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2173,6 +2173,26 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `type` | string | false | | | | `validate_url` | string | false | | | +## codersdk.GitSSHKey + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "public_key": "string", + "updated_at": "2019-08-24T14:15:22Z", + "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------ | ------ | -------- | ------------ | ----------- | +| `created_at` | string | false | | | +| `public_key` | string | false | | | +| `updated_at` | string | false | | | +| `user_id` | string | false | | | + ## codersdk.GoogleInstanceIdentityToken ```json diff --git a/docs/api/users.md b/docs/api/users.md index f1c1880ac0596..71e9ac7e791be 100644 --- a/docs/api/users.md +++ b/docs/api/users.md @@ -421,6 +421,86 @@ curl -X DELETE http://coder-server:8080/api/v2/users/{user} \ To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Get user Git SSH key + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/{user}/gitsshkey \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/{user}/gitsshkey` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | + +### Example responses + +> 200 Response + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "public_key": "string", + "updated_at": "2019-08-24T14:15:22Z", + "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.GitSSHKey](schemas.md#codersdkgitsshkey) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Regenerate user SSH key + +### Code samples + +```shell +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/users/{user}/gitsshkey \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /users/{user}/gitsshkey` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | + +### Example responses + +> 200 Response + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "public_key": "string", + "updated_at": "2019-08-24T14:15:22Z", + "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.GitSSHKey](schemas.md#codersdkgitsshkey) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Create new session key ### Code samples From c06a6360788ace8091245057a097f273b2fb70e0 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 12:14:37 +0100 Subject: [PATCH 32/61] All --- coderd/apidoc/docs.go | 135 ++++++++++++++++++++++++++++++++-- coderd/apidoc/swagger.json | 123 +++++++++++++++++++++++++++++-- coderd/apikey.go | 2 +- coderd/users.go | 18 +++++ coderd/workspacebuilds.go | 10 +++ coderd/workspaces.go | 6 +- codersdk/organizations.go | 6 +- docs/api/builds.md | 144 +++++++++++++++++++++++++++++++++++++ docs/api/users.md | 95 ++++++++++++++++++++++++ docs/api/workspaces.md | 4 +- docs/manifest.json | 8 +-- 11 files changed, 530 insertions(+), 21 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 2bbf76f8294dd..edbd05390fc49 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2384,7 +2384,7 @@ const docTemplate = `{ "Users" ], "summary": "Get API key", - "operationId": "get-user-tokens", + "operationId": "get-api-key", "parameters": [ { "type": "string", @@ -2449,6 +2449,84 @@ const docTemplate = `{ } } }, + "/users/{user}/organizations": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get organizations by user", + "operationId": "get-organizations-by-users", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Organization" + } + } + } + } + } + }, + "/users/{user}/organizations/{organizationname}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get organization by user and organization name", + "operationId": "get-organization-by-user-and-organization-name", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Organization name", + "name": "organizationname", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Organization" + } + } + } + } + }, "/users/{user}/password": { "put": { "security": [ @@ -2691,12 +2769,12 @@ const docTemplate = `{ "tags": [ "Workspaces" ], - "summary": "Get workspace metadata by owner and workspace name", - "operationId": "get-workspace-metadata-by-owner-and-workspace-name", + "summary": "Get workspace metadata by user and workspace name", + "operationId": "get-workspace-metadata-by-user-and-workspace-name", "parameters": [ { "type": "string", - "description": "Owner username", + "description": "User ID, name, or me", "name": "user", "in": "path", "required": true @@ -2725,6 +2803,55 @@ const docTemplate = `{ } } }, + "/users/{user}/workspace/{workspacename}/builds/{buildnumber}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Builds" + ], + "summary": "Get workspace build by user, workspace name, and build number", + "operationId": "get-workspace-build-by-user-workspace-name-and-build-number", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Workspace name", + "name": "workspacename", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "number", + "description": "Build number", + "name": "buildnumber", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceBuild" + } + } + } + } + }, "/workspaceagents/aws-instance-identity": { "post": { "security": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 736e74bbef4e1..2acda213e26b8 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -2092,7 +2092,7 @@ "produces": ["application/json"], "tags": ["Users"], "summary": "Get API key", - "operationId": "get-user-tokens", + "operationId": "get-api-key", "parameters": [ { "type": "string", @@ -2153,6 +2153,76 @@ } } }, + "/users/{user}/organizations": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get organizations by user", + "operationId": "get-organizations-by-users", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Organization" + } + } + } + } + } + }, + "/users/{user}/organizations/{organizationname}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get organization by user and organization name", + "operationId": "get-organization-by-user-and-organization-name", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Organization name", + "name": "organizationname", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Organization" + } + } + } + } + }, "/users/{user}/password": { "put": { "security": [ @@ -2365,12 +2435,12 @@ ], "produces": ["application/json"], "tags": ["Workspaces"], - "summary": "Get workspace metadata by owner and workspace name", - "operationId": "get-workspace-metadata-by-owner-and-workspace-name", + "summary": "Get workspace metadata by user and workspace name", + "operationId": "get-workspace-metadata-by-user-and-workspace-name", "parameters": [ { "type": "string", - "description": "Owner username", + "description": "User ID, name, or me", "name": "user", "in": "path", "required": true @@ -2399,6 +2469,51 @@ } } }, + "/users/{user}/workspace/{workspacename}/builds/{buildnumber}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Builds"], + "summary": "Get workspace build by user, workspace name, and build number", + "operationId": "get-workspace-build-by-user-workspace-name-and-build-number", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Workspace name", + "name": "workspacename", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "number", + "description": "Build number", + "name": "buildnumber", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceBuild" + } + } + } + } + }, "/workspaceagents/aws-instance-identity": { "post": { "security": [ diff --git a/coderd/apikey.go b/coderd/apikey.go index d759794e73838..47ffcd201576e 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -132,7 +132,7 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { } // @Summary Get API key -// @ID get-user-tokens +// @ID get-api-key // @Security CoderSessionToken // @Produce json // @Tags Users diff --git a/coderd/users.go b/coderd/users.go index 093681c55ceb5..3e6acf34d02c7 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -906,6 +906,15 @@ func (api *API) updateSiteUserRoles(ctx context.Context, args database.UpdateUse } // Returns organizations the parameterized user has access to. +// +// @Summary Get organizations by user +// @ID get-organizations-by-users +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Success 200 {array} codersdk.Organization +// @Router /users/{user}/organizations [get] func (api *API) organizationsByUser(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() user := httpmw.UserParam(r) @@ -941,6 +950,15 @@ func (api *API) organizationsByUser(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, publicOrganizations) } +// @Summary Get organization by user and organization name +// @ID get-organization-by-user-and-organization-name +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Param organizationname path string true "Organization name" +// @Success 200 {object} codersdk.Organization +// @Router /users/{user}/organizations/{organizationname} [get] func (api *API) organizationByUserAndName(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organizationName := chi.URLParam(r, "organizationname") diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 4f057786ddfe1..3d3d42b7ea2a1 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -191,6 +191,16 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, apiBuilds) } +// @Summary Get workspace build by user, workspace name, and build number +// @ID get-workspace-build-by-user-workspace-name-and-build-number +// @Security CoderSessionToken +// @Produce json +// @Tags Builds +// @Param user path string true "User ID, name, or me" +// @Param workspacename path string true "Workspace name" +// @Param buildnumber path string true "Build number" format(number) +// @Success 200 {object} codersdk.WorkspaceBuild +// @Router /users/{user}/workspace/{workspacename}/builds/{buildnumber} [get] func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() owner := httpmw.UserParam(r) diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 6edd0d13c629b..6ef629fe47bd0 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -192,12 +192,12 @@ func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) { }) } -// @Summary Get workspace metadata by owner and workspace name -// @ID get-workspace-metadata-by-owner-and-workspace-name +// @Summary Get workspace metadata by user and workspace name +// @ID get-workspace-metadata-by-user-and-workspace-name // @Security CoderSessionToken // @Produce json // @Tags Workspaces -// @Param user path string true "Owner username" +// @Param user path string true "User ID, name, or me" // @Param workspacename path string true "Workspace name" // @Param include_deleted query bool false "Return data instead of HTTP 404 if the workspace is deleted" // @Success 200 {object} codersdk.Workspace diff --git a/codersdk/organizations.go b/codersdk/organizations.go index 39b50da013ec5..27a30f8a14583 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -36,9 +36,9 @@ type Organization struct { type CreateTemplateVersionRequest struct { Name string `json:"name,omitempty" validate:"omitempty,template_name"` // TemplateID optionally associates a version with a template. - TemplateID uuid.UUID `json:"template_id,omitempty"` - StorageMethod ProvisionerStorageMethod `json:"storage_method" validate:"oneof=file,required"` - FileID uuid.UUID `json:"file_id,omitempty" validate:"required_without=ExampleID"` + TemplateID uuid.UUID `json:"template_id,omitempty" format:"uuid"` + StorageMethod ProvisionerStorageMethod `json:"storage_method" validate:"oneof=file,required" enums:"file"` + FileID uuid.UUID `json:"file_id,omitempty" validate:"required_without=ExampleID" format:"uuid"` ExampleID string `json:"example_id,omitempty" validate:"required_without=FileID"` Provisioner ProvisionerType `json:"provisioner" validate:"oneof=terraform echo,required"` ProvisionerTags map[string]string `json:"tags"` diff --git a/docs/api/builds.md b/docs/api/builds.md index 366292529d971..5a58c4b33291f 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -2,6 +2,150 @@ > This page is incomplete, stay tuned. +## Get workspace build by user, workspace name, and build number + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacename}/builds/{buildnumber} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/{user}/workspace/{workspacename}/builds/{buildnumber}` + +### Parameters + +| Name | In | Type | Required | Description | +| --------------- | ---- | -------------- | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | +| `workspacename` | path | string | true | Workspace name | +| `buildnumber` | path | string(number) | true | Build number | + +### Example responses + +> 200 Response + +```json +{ + "build_number": 0, + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "deadline": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", + "initiator_name": "string", + "job": { + "canceled_at": "2019-08-24T14:15:22Z", + "completed_at": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z", + "error": "string", + "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "started_at": "2019-08-24T14:15:22Z", + "status": "pending", + "tags": { + "property1": "string", + "property2": "string" + }, + "worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b" + }, + "reason": "initiator", + "resources": [ + { + "agents": [ + { + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "string", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "architecture": "string", + "connection_timeout_seconds": 0, + "created_at": "2019-08-24T14:15:22Z", + "directory": "string", + "disconnected_at": "2019-08-24T14:15:22Z", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "instance_id": "string", + "last_connected_at": "2019-08-24T14:15:22Z", + "latency": { + "property1": { + "latency_ms": 0, + "preferred": true + }, + "property2": { + "latency_ms": 0, + "preferred": true + } + }, + "name": "string", + "operating_system": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", + "startup_script": "string", + "status": "connecting", + "troubleshooting_url": "string", + "updated_at": "2019-08-24T14:15:22Z", + "version": "string" + } + ], + "created_at": "2019-08-24T14:15:22Z", + "daily_cost": 0, + "hide": true, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f", + "metadata": [ + { + "key": "string", + "sensitive": true, + "value": "string" + } + ], + "name": "string", + "type": "string", + "workspace_transition": "start" + } + ], + "status": "pending", + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", + "template_version_name": "string", + "transition": "start", + "updated_at": "2019-08-24T14:15:22Z", + "workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9", + "workspace_name": "string", + "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", + "workspace_owner_name": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceBuild](schemas.md#codersdkworkspacebuild) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Get workspace build ### Code samples diff --git a/docs/api/users.md b/docs/api/users.md index 71e9ac7e791be..9665d99be9ed7 100644 --- a/docs/api/users.md +++ b/docs/api/users.md @@ -734,6 +734,101 @@ curl -X DELETE http://coder-server:8080/api/v2/users/{user}/keys/{keyid} \ To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Get organizations by user + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/{user}/organizations \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/{user}/organizations` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "updated_at": "2019-08-24T14:15:22Z" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Organization](schemas.md#codersdkorganization) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| -------------- | ----------------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | true | | | +| `» id` | string(uuid) | true | | | +| `» name` | string | true | | | +| `» updated_at` | string(date-time) | true | | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get organization by user and organization name + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/{user}/organizations/{organizationname} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/{user}/organizations/{organizationname}` + +### Parameters + +| Name | In | Type | Required | Description | +| ------------------ | ---- | ------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | +| `organizationname` | path | string | true | Organization name | + +### Example responses + +> 200 Response + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "updated_at": "2019-08-24T14:15:22Z" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Organization](schemas.md#codersdkorganization) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Update user password ### Code samples diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 915741e2209ae..1e46376edc726 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -162,7 +162,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Get workspace metadata by owner and workspace name +## Get workspace metadata by user and workspace name ### Code samples @@ -179,7 +179,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam | Name | In | Type | Required | Description | | ----------------- | ----- | ------- | -------- | ----------------------------------------------------------- | -| `user` | path | string | true | Owner username | +| `user` | path | string | true | User ID, name, or me | | `workspacename` | path | string | true | Workspace name | | `include_deleted` | query | boolean | false | Return data instead of HTTP 404 if the workspace is deleted | diff --git a/docs/manifest.json b/docs/manifest.json index 22546066c1dee..a06a5d7bddb58 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -377,14 +377,14 @@ "title": "Authentication", "path": "./api/authentication.md" }, - { - "title": "Agents", - "path": "./api/agents.md" - }, { "title": "Builds", "path": "./api/builds.md" }, + { + "title": "Agents", + "path": "./api/agents.md" + }, { "title": "Schemas", "path": "./api/schemas.md" From ee6dbb198cd2375128fa17657d770429bff7f51b Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 12:25:33 +0100 Subject: [PATCH 33/61] Typo --- coderd/apikey.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/apikey.go b/coderd/apikey.go index 47ffcd201576e..e4cbd7552c2cc 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -125,7 +125,7 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { } // We intentionally do not set the cookie on the response here. - // Setting the cookie will couple the browser sesion to the API + // Setting the cookie will couple the browser session to the API // key we return here, meaning logging out of the website would // invalid your CLI key. httpapi.Write(ctx, rw, http.StatusCreated, codersdk.GenerateAPIKeyResponse{Key: cookie.Value}) From 8f22f8d3d6f0e4f91cf2f17aefb3af8f22ac8139 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 12:46:43 +0100 Subject: [PATCH 34/61] Fix --- cli/userstatus_test.go | 6 +++--- coderd/users.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/userstatus_test.go b/cli/userstatus_test.go index aea26f8387228..05fe8898c82e6 100644 --- a/cli/userstatus_test.go +++ b/cli/userstatus_test.go @@ -33,7 +33,7 @@ func TestUserStatus(t *testing.T) { }) t.Run("StatusOther", func(t *testing.T) { - require.Equal(t, otherUser.Status, codersdk.UserStatusActive, "start as active") + require.Equal(t, codersdk.UserStatusActive, otherUser.Status, "start as active") cmd, root := clitest.New(t, "users", "suspend", otherUser.Username) clitest.SetupConfig(t, client, root) @@ -45,7 +45,7 @@ func TestUserStatus(t *testing.T) { // Check the user status otherUser, err = client.User(context.Background(), otherUser.Username) require.NoError(t, err, "fetch suspended user") - require.Equal(t, otherUser.Status, codersdk.UserStatusSuspended, "suspended user") + require.Equal(t, codersdk.UserStatusSuspended, otherUser.Status, "suspended user") // Set back to active. Try using a uuid as well cmd, root = clitest.New(t, "users", "activate", otherUser.ID.String()) @@ -58,6 +58,6 @@ func TestUserStatus(t *testing.T) { // Check the user status otherUser, err = client.User(context.Background(), otherUser.ID.String()) require.NoError(t, err, "fetch active user") - require.Equal(t, otherUser.Status, codersdk.UserStatusActive, "active user") + require.Equal(t, codersdk.UserStatusActive, otherUser.Status, "active user") }) } diff --git a/coderd/users.go b/coderd/users.go index 3e6acf34d02c7..759299dc0fc24 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -542,7 +542,7 @@ func (api *API) putSuspendUserAccount() func(rw http.ResponseWriter, r *http.Req // @Success 200 {object} codersdk.User // @Router /users/{user}/status/activate [put] func (api *API) putActivateUserAccount() func(rw http.ResponseWriter, r *http.Request) { - return api.putUserStatus(database.UserStatusSuspended) + return api.putUserStatus(database.UserStatusActive) } func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseWriter, r *http.Request) { From 77be926a08b44e3711c84a36b239ac0fc48825bf Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 13:37:22 +0100 Subject: [PATCH 35/61] Entitlements --- Makefile | 2 +- coderd/apidoc/docs.go | 74 +++++++++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 70 +++++++++++++++++++++++++++++++++ docs/api/enterprise.md | 52 ++++++++++++++++++++++++ docs/api/schemas.md | 58 +++++++++++++++++++++++++++ docs/manifest.json | 4 ++ enterprise/coderd/coderd.go | 7 ++++ scripts/apidocgen/generate.sh | 2 +- 8 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 docs/api/enterprise.md diff --git a/Makefile b/Makefile index 40a981afd59d8..cd7c316a4e266 100644 --- a/Makefile +++ b/Makefile @@ -483,7 +483,7 @@ docs/admin/prometheus.md: scripts/metricsdocgen/main.go scripts/metricsdocgen/me cd site yarn run format:write:only ../docs/admin/prometheus.md -coderd/apidoc/swagger.json: $(shell find ./scripts/apidocgen -not \( -path './scripts/apidocgen/node_modules' -prune \) -type f) $(wildcard coderd/*.go) $(wildcard codersdk/*.go) .swaggo +coderd/apidoc/swagger.json: $(shell find ./scripts/apidocgen -not \( -path './scripts/apidocgen/node_modules' -prune \) -type f) $(wildcard coderd/*.go) $(wildcard enterprise/coderd/*.go) $(wildcard codersdk/*.go) .swaggo ./scripts/apidocgen/generate.sh cd site yarn run format:write:only ../docs/api ../docs/manifest.json ../coderd/apidoc/swagger.json diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index edbd05390fc49..ac59a6f3525cc 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -303,6 +303,31 @@ const docTemplate = `{ } } }, + "/entitlements": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get entitlements", + "operationId": "get-entitlements", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Entitlements" + } + } + } + } + }, "/files": { "post": { "security": [ @@ -4788,6 +4813,55 @@ const docTemplate = `{ } } }, + "codersdk.Entitlements": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "type": "string" + } + }, + "experimental": { + "type": "boolean" + }, + "features": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/codersdk.Feature" + } + }, + "has_license": { + "type": "boolean" + }, + "trial": { + "type": "boolean" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "codersdk.Feature": { + "type": "object", + "properties": { + "actual": { + "type": "integer" + }, + "enabled": { + "type": "boolean" + }, + "entitlement": { + "type": "string" + }, + "limit": { + "type": "integer" + } + } + }, "codersdk.GenerateAPIKeyResponse": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 2acda213e26b8..ff1a606c48ab5 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -257,6 +257,27 @@ } } }, + "/entitlements": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get entitlements", + "operationId": "get-entitlements", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Entitlements" + } + } + } + } + }, "/files": { "post": { "security": [ @@ -4279,6 +4300,55 @@ } } }, + "codersdk.Entitlements": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "type": "string" + } + }, + "experimental": { + "type": "boolean" + }, + "features": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/codersdk.Feature" + } + }, + "has_license": { + "type": "boolean" + }, + "trial": { + "type": "boolean" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "codersdk.Feature": { + "type": "object", + "properties": { + "actual": { + "type": "integer" + }, + "enabled": { + "type": "boolean" + }, + "entitlement": { + "type": "string" + }, + "limit": { + "type": "integer" + } + } + }, "codersdk.GenerateAPIKeyResponse": { "type": "object", "properties": { diff --git a/docs/api/enterprise.md b/docs/api/enterprise.md new file mode 100644 index 0000000000000..b1cdfab2172ea --- /dev/null +++ b/docs/api/enterprise.md @@ -0,0 +1,52 @@ +# Enterprise + +> This page is incomplete, stay tuned. + +## Get entitlements + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/entitlements \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /entitlements` + +### Example responses + +> 200 Response + +```json +{ + "errors": ["string"], + "experimental": true, + "features": { + "property1": { + "actual": 0, + "enabled": true, + "entitlement": "string", + "limit": 0 + }, + "property2": { + "actual": 0, + "enabled": true, + "entitlement": "string", + "limit": 0 + } + }, + "has_license": true, + "trial": true, + "warnings": ["string"] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Entitlements](schemas.md#codersdkentitlements) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 08fc7bbc13624..c6eb8b47c2bce 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2082,6 +2082,64 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `usage` | string | false | | | | `value` | integer | false | | | +## codersdk.Entitlements + +```json +{ + "errors": ["string"], + "experimental": true, + "features": { + "property1": { + "actual": 0, + "enabled": true, + "entitlement": "string", + "limit": 0 + }, + "property2": { + "actual": 0, + "enabled": true, + "entitlement": "string", + "limit": 0 + } + }, + "has_license": true, + "trial": true, + "warnings": ["string"] +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | ------------------------------------ | -------- | ------------ | ----------- | +| `errors` | array of string | false | | | +| `experimental` | boolean | false | | | +| `features` | object | false | | | +| » `[any property]` | [codersdk.Feature](#codersdkfeature) | false | | | +| `has_license` | boolean | false | | | +| `trial` | boolean | false | | | +| `warnings` | array of string | false | | | + +## codersdk.Feature + +```json +{ + "actual": 0, + "enabled": true, + "entitlement": "string", + "limit": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------- | ------- | -------- | ------------ | ----------- | +| `actual` | integer | false | | | +| `enabled` | boolean | false | | | +| `entitlement` | string | false | | | +| `limit` | integer | false | | | + ## codersdk.GenerateAPIKeyResponse ```json diff --git a/docs/manifest.json b/docs/manifest.json index a06a5d7bddb58..02d7f32110433 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -345,6 +345,10 @@ "title": "Authorization", "path": "./api/authorization.md" }, + { + "title": "Enterprise", + "path": "./api/enterprise.md" + }, { "title": "Files", "path": "./api/files.md" diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 0ea4eff280357..afb3c4c64f816 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -331,6 +331,13 @@ func (api *API) updateEntitlements(ctx context.Context) error { return nil } +// @Summary Get entitlements +// @ID get-entitlements +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Success 200 {object} codersdk.Entitlements +// @Router /entitlements [get] func (api *API) serveEntitlements(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() api.entitlementsMu.RLock() diff --git a/scripts/apidocgen/generate.sh b/scripts/apidocgen/generate.sh index a02ecf131b801..c58718b59a5bd 100755 --- a/scripts/apidocgen/generate.sh +++ b/scripts/apidocgen/generate.sh @@ -20,7 +20,7 @@ log "Use temporary file: ${API_MD_TMP_FILE}" pushd "${PROJECT_ROOT}" go run github.com/swaggo/swag/cmd/swag@v1.8.6 init \ --generalInfo="coderd.go" \ - --dir="./coderd,./codersdk" \ + --dir="./coderd,./codersdk,./enterprise/coderd" \ --output="./coderd/apidoc" \ --outputTypes="go,json" \ --parseDependency=true From 4fcedb054189a91727a601464ff90d53aa944f0a Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 14:42:07 +0100 Subject: [PATCH 36/61] Groups --- coderd/apidoc/docs.go | 779 ++++++++++++++++++++++-- coderd/apidoc/swagger.json | 636 ++++++++++++++++++- codersdk/groups.go | 4 +- codersdk/licenses.go | 4 +- codersdk/provisionerdaemons.go | 6 +- codersdk/replicas.go | 4 +- codersdk/templates.go | 4 +- docs/api/enterprise.md | 669 ++++++++++++++++++++ docs/api/organizations.md | 49 ++ docs/api/schemas.md | 245 ++++++++ docs/api/templates.md | 71 +++ docs/manifest.json | 8 +- enterprise/coderd/coderd.go | 2 +- enterprise/coderd/groups.go | 51 ++ enterprise/coderd/licenses.go | 24 + enterprise/coderd/provisionerdaemons.go | 17 + enterprise/coderd/replicas.go | 8 + enterprise/coderd/templates.go | 17 + 18 files changed, 2545 insertions(+), 53 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index ac59a6f3525cc..beaedc3060963 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -403,6 +403,172 @@ const docTemplate = `{ } } }, + "/groups": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get groups", + "operationId": "get-groups", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + } + } + }, + "/groups/{groupName}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get group by name", + "operationId": "get-group-by-name", + "parameters": [ + { + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + } + }, + "/license/{id}": { + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Delete license", + "operationId": "delete-license", + "parameters": [ + { + "type": "string", + "format": "number", + "description": "License ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/licenses": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get licenses", + "operationId": "get-licenses", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.License" + } + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Organizations" + ], + "summary": "Add new license", + "operationId": "add-new-license", + "parameters": [ + { + "description": "Add license request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AddLicenseRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.License" + } + } + } + } + }, "/organizations": { "post": { "security": [ @@ -474,6 +640,130 @@ const docTemplate = `{ } } }, + "/organizations/{organization}/groups": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get groups by organization", + "operationId": "get-groups-by-organization", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Create group for organization", + "operationId": "create-group-for-organization", + "parameters": [ + { + "description": "Create group request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateGroupRequest" + } + }, + { + "type": "string", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + } + }, + "/organizations/{organization}/groups/{groupName}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get group by organization and name", + "operationId": "get-group-by-organization-and-name", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + } + }, "/organizations/{organization}/members/roles": { "get": { "security": [ @@ -607,6 +897,76 @@ const docTemplate = `{ } } }, + "/organizations/{organization}/provisionerdaemons": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get provisioner daemons", + "operationId": "get-provisioner-daemons", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ProvisionerDaemon" + } + } + } + } + } + }, + "/organizations/{organization}/provisionerdaemons/serve": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Serve provisioner daemon", + "operationId": "serve-provisioner-daemon", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "101": { + "description": "Switching Protocols" + } + } + } + }, "/organizations/{organization}/templates": { "get": { "security": [ @@ -1061,6 +1421,34 @@ const docTemplate = `{ } } }, + "/replicas": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get active replicas", + "operationId": "get-active-replicas", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Replica" + } + } + } + } + } + }, "/templates/{id}": { "get": { "security": [ @@ -1252,39 +1640,119 @@ const docTemplate = `{ } } } - }, - "patch": { + }, + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Update active template version by template ID", + "operationId": "update-active-template-version-by-template-ID", + "parameters": [ + { + "description": "Modified template version", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateActiveTemplateVersion" + } + }, + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, + "/templates/{id}/versions/{templateversionname}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get template version by template ID and name", + "operationId": "get-template-version-by-template-id-and-name", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Template version name", + "name": "templateversionname", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } + } + } + } + } + }, + "/templates/{template}/acl": { + "get": { "security": [ { "CoderSessionToken": [] } ], - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], "tags": [ - "Templates" + "Enterprise" ], - "summary": "Update active template version by template ID", - "operationId": "update-active-template-version-by-template-ID", + "summary": "Get template ACLs", + "operationId": "get-template-acls", "parameters": [ - { - "description": "Modified template version", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateActiveTemplateVersion" - } - }, { "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true } @@ -1293,14 +1761,15 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateUser" + } } } } - } - }, - "/templates/{id}/versions/{templateversionname}": { - "get": { + }, + "patch": { "security": [ { "CoderSessionToken": [] @@ -1310,35 +1779,34 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Templates" + "Enterprise" ], - "summary": "Get template version by template ID and name", - "operationId": "get-template-version-by-template-id-and-name", + "summary": "Update template ACL", + "operationId": "update-template-acl", "parameters": [ { "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true }, { - "type": "string", - "description": "Template version name", - "name": "templateversionname", - "in": "path", - "required": true + "description": "Update template request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateTemplateACL" + } } ], "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.TemplateVersion" - } + "$ref": "#/definitions/codersdk.Response" } } } @@ -3927,6 +4395,17 @@ const docTemplate = `{ } } }, + "codersdk.AddLicenseRequest": { + "type": "object", + "required": [ + "license" + ], + "properties": { + "license": { + "type": "string" + } + } + }, "codersdk.AgentGitSSHKey": { "type": "object", "properties": { @@ -4222,6 +4701,20 @@ const docTemplate = `{ } } }, + "codersdk.CreateGroupRequest": { + "type": "object", + "properties": { + "avatar_url": { + "type": "string" + }, + "name": { + "type": "string" + }, + "quota_allowance": { + "type": "integer" + } + } + }, "codersdk.CreateOrganizationRequest": { "type": "object", "required": [ @@ -4959,6 +5452,34 @@ const docTemplate = `{ } } }, + "codersdk.Group": { + "type": "object", + "properties": { + "avatar_url": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "members": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.User" + } + }, + "name": { + "type": "string" + }, + "organization_id": { + "type": "string", + "format": "uuid" + }, + "quota_allowance": { + "type": "integer" + } + } + }, "codersdk.Healthcheck": { "type": "object", "properties": { @@ -4976,6 +5497,27 @@ const docTemplate = `{ } } }, + "codersdk.License": { + "type": "object", + "properties": { + "claims": { + "description": "Claims are the JWT claims asserted by the license. Here we use\na generic string map to ensure that all data from the server is\nparsed verbatim, not just the fields this version of Coder\nunderstands.", + "type": "object", + "additionalProperties": true + }, + "id": { + "type": "integer" + }, + "uploaded_at": { + "type": "string", + "format": "date-time" + }, + "uuid": { + "type": "string", + "format": "uuid" + } + } + }, "codersdk.LoginWithPasswordRequest": { "type": "object", "required": [ @@ -5298,6 +5840,38 @@ const docTemplate = `{ } } }, + "codersdk.ProvisionerDaemon": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "provisioners": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "updated_at": { + "format": "date-time", + "$ref": "#/definitions/sql.NullTime" + } + } + }, "codersdk.ProvisionerJob": { "type": "object", "properties": { @@ -5393,6 +5967,41 @@ const docTemplate = `{ } } }, + "codersdk.Replica": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt is when the replica was first seen.", + "type": "string", + "format": "date-time" + }, + "database_latency": { + "description": "DatabaseLatency is the latency in microseconds to the database.", + "type": "integer" + }, + "error": { + "description": "Error is the error.", + "type": "string" + }, + "hostname": { + "description": "Hostname is the hostname of the replica.", + "type": "string" + }, + "id": { + "description": "ID is the unique identifier for the replica.", + "type": "string", + "format": "uuid" + }, + "region_id": { + "description": "RegionID is the region of the replica.", + "type": "integer" + }, + "relay_address": { + "description": "RelayAddress is the accessible address to relay DERP connections.", + "type": "string" + } + } + }, "codersdk.Response": { "type": "object", "properties": { @@ -5591,6 +6200,67 @@ const docTemplate = `{ } } }, + "codersdk.TemplateUser": { + "type": "object", + "required": [ + "created_at", + "email", + "id", + "username" + ], + "properties": { + "avatar_url": { + "type": "string", + "format": "uri" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "email": { + "type": "string", + "format": "email" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "last_seen_at": { + "type": "string", + "format": "date-time" + }, + "organization_ids": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + }, + "role": { + "type": "string", + "enum": [ + "admin", + "use" + ] + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Role" + } + }, + "status": { + "type": "string", + "enum": [ + "active", + "suspended" + ] + }, + "username": { + "type": "string" + } + } + }, "codersdk.TemplateVersion": { "type": "object", "properties": { @@ -5695,6 +6365,31 @@ const docTemplate = `{ } } }, + "codersdk.UpdateTemplateACL": { + "type": "object", + "properties": { + "group_perms": { + "type": "object", + "additionalProperties": { + "type": "string", + "enum": [ + "admin", + "use" + ] + } + }, + "user_perms": { + "type": "object", + "additionalProperties": { + "type": "string", + "enum": [ + "admin", + "use" + ] + } + } + } + }, "codersdk.UpdateUserPasswordRequest": { "type": "object", "required": [ @@ -6294,6 +6989,18 @@ const docTemplate = `{ } } }, + "sql.NullTime": { + "type": "object", + "properties": { + "time": { + "type": "string" + }, + "valid": { + "description": "Valid is true if Time is not NULL", + "type": "boolean" + } + } + }, "tailcfg.DERPMap": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index ff1a606c48ab5..417e6d47b2ec1 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -345,6 +345,152 @@ } } }, + "/groups": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get groups", + "operationId": "get-groups", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + } + } + }, + "/groups/{groupName}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get group by name", + "operationId": "get-group-by-name", + "parameters": [ + { + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + } + }, + "/license/{id}": { + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Delete license", + "operationId": "delete-license", + "parameters": [ + { + "type": "string", + "format": "number", + "description": "License ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/licenses": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get licenses", + "operationId": "get-licenses", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.License" + } + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Organizations"], + "summary": "Add new license", + "operationId": "add-new-license", + "parameters": [ + { + "description": "Add license request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AddLicenseRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.License" + } + } + } + } + }, "/organizations": { "post": { "security": [ @@ -408,6 +554,116 @@ } } }, + "/organizations/{organization}/groups": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get groups by organization", + "operationId": "get-groups-by-organization", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + } + }, + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Create group for organization", + "operationId": "create-group-for-organization", + "parameters": [ + { + "description": "Create group request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateGroupRequest" + } + }, + { + "type": "string", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + } + }, + "/organizations/{organization}/groups/{groupName}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get group by organization and name", + "operationId": "get-group-by-organization-and-name", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + } + }, "/organizations/{organization}/members/roles": { "get": { "security": [ @@ -527,6 +783,68 @@ } } }, + "/organizations/{organization}/provisionerdaemons": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get provisioner daemons", + "operationId": "get-provisioner-daemons", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ProvisionerDaemon" + } + } + } + } + } + }, + "/organizations/{organization}/provisionerdaemons/serve": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Serve provisioner daemon", + "operationId": "serve-provisioner-daemon", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "101": { + "description": "Switching Protocols" + } + } + } + }, "/organizations/{organization}/templates": { "get": { "security": [ @@ -917,7 +1235,31 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, + "/replicas": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get active replicas", + "operationId": "get-active-replicas", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Replica" + } } } } @@ -1176,6 +1518,78 @@ } } }, + "/templates/{template}/acl": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get template ACLs", + "operationId": "get-template-acls", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "template", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateUser" + } + } + } + } + }, + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Update template ACL", + "operationId": "update-template-acl", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "template", + "in": "path", + "required": true + }, + { + "description": "Update template request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateTemplateACL" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, "/templateversions/{templateversion}": { "get": { "security": [ @@ -3459,6 +3873,15 @@ } } }, + "codersdk.AddLicenseRequest": { + "type": "object", + "required": ["license"], + "properties": { + "license": { + "type": "string" + } + } + }, "codersdk.AgentGitSSHKey": { "type": "object", "properties": { @@ -3742,6 +4165,20 @@ } } }, + "codersdk.CreateGroupRequest": { + "type": "object", + "properties": { + "avatar_url": { + "type": "string" + }, + "name": { + "type": "string" + }, + "quota_allowance": { + "type": "integer" + } + } + }, "codersdk.CreateOrganizationRequest": { "type": "object", "required": ["name"], @@ -4444,6 +4881,34 @@ } } }, + "codersdk.Group": { + "type": "object", + "properties": { + "avatar_url": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "members": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.User" + } + }, + "name": { + "type": "string" + }, + "organization_id": { + "type": "string", + "format": "uuid" + }, + "quota_allowance": { + "type": "integer" + } + } + }, "codersdk.Healthcheck": { "type": "object", "properties": { @@ -4461,6 +4926,27 @@ } } }, + "codersdk.License": { + "type": "object", + "properties": { + "claims": { + "description": "Claims are the JWT claims asserted by the license. Here we use\na generic string map to ensure that all data from the server is\nparsed verbatim, not just the fields this version of Coder\nunderstands.", + "type": "object", + "additionalProperties": true + }, + "id": { + "type": "integer" + }, + "uploaded_at": { + "type": "string", + "format": "date-time" + }, + "uuid": { + "type": "string", + "format": "uuid" + } + } + }, "codersdk.LoginWithPasswordRequest": { "type": "object", "required": ["email", "password"], @@ -4755,6 +5241,38 @@ } } }, + "codersdk.ProvisionerDaemon": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "provisioners": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "updated_at": { + "format": "date-time", + "$ref": "#/definitions/sql.NullTime" + } + } + }, "codersdk.ProvisionerJob": { "type": "object", "properties": { @@ -4842,6 +5360,41 @@ } } }, + "codersdk.Replica": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt is when the replica was first seen.", + "type": "string", + "format": "date-time" + }, + "database_latency": { + "description": "DatabaseLatency is the latency in microseconds to the database.", + "type": "integer" + }, + "error": { + "description": "Error is the error.", + "type": "string" + }, + "hostname": { + "description": "Hostname is the hostname of the replica.", + "type": "string" + }, + "id": { + "description": "ID is the unique identifier for the replica.", + "type": "string", + "format": "uuid" + }, + "region_id": { + "description": "RegionID is the region of the replica.", + "type": "integer" + }, + "relay_address": { + "description": "RelayAddress is the accessible address to relay DERP connections.", + "type": "string" + } + } + }, "codersdk.Response": { "type": "object", "properties": { @@ -5040,6 +5593,56 @@ } } }, + "codersdk.TemplateUser": { + "type": "object", + "required": ["created_at", "email", "id", "username"], + "properties": { + "avatar_url": { + "type": "string", + "format": "uri" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "email": { + "type": "string", + "format": "email" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "last_seen_at": { + "type": "string", + "format": "date-time" + }, + "organization_ids": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + }, + "role": { + "type": "string", + "enum": ["admin", "use"] + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Role" + } + }, + "status": { + "type": "string", + "enum": ["active", "suspended"] + }, + "username": { + "type": "string" + } + } + }, "codersdk.TemplateVersion": { "type": "object", "properties": { @@ -5142,6 +5745,25 @@ } } }, + "codersdk.UpdateTemplateACL": { + "type": "object", + "properties": { + "group_perms": { + "type": "object", + "additionalProperties": { + "type": "string", + "enum": ["admin", "use"] + } + }, + "user_perms": { + "type": "object", + "additionalProperties": { + "type": "string", + "enum": ["admin", "use"] + } + } + } + }, "codersdk.UpdateUserPasswordRequest": { "type": "object", "required": ["password"], @@ -5705,6 +6327,18 @@ } } }, + "sql.NullTime": { + "type": "object", + "properties": { + "time": { + "type": "string" + }, + "valid": { + "description": "Valid is true if Time is not NULL", + "type": "boolean" + } + } + }, "tailcfg.DERPMap": { "type": "object", "properties": { diff --git a/codersdk/groups.go b/codersdk/groups.go index 0647efd6e9d93..b0608f3530a3d 100644 --- a/codersdk/groups.go +++ b/codersdk/groups.go @@ -17,9 +17,9 @@ type CreateGroupRequest struct { } type Group struct { - ID uuid.UUID `json:"id"` + ID uuid.UUID `json:"id" format:"uuid"` Name string `json:"name"` - OrganizationID uuid.UUID `json:"organization_id"` + OrganizationID uuid.UUID `json:"organization_id" format:"uuid"` Members []User `json:"members"` AvatarURL string `json:"avatar_url"` QuotaAllowance int `json:"quota_allowance"` diff --git a/codersdk/licenses.go b/codersdk/licenses.go index 73118de5fdb32..3ede213e53251 100644 --- a/codersdk/licenses.go +++ b/codersdk/licenses.go @@ -16,8 +16,8 @@ type AddLicenseRequest struct { type License struct { ID int32 `json:"id"` - UUID uuid.UUID `json:"uuid"` - UploadedAt time.Time `json:"uploaded_at"` + UUID uuid.UUID `json:"uuid" format:"uuid"` + UploadedAt time.Time `json:"uploaded_at" format:"date-time"` // Claims are the JWT claims asserted by the license. Here we use // a generic string map to ensure that all data from the server is // parsed verbatim, not just the fields this version of Coder diff --git a/codersdk/provisionerdaemons.go b/codersdk/provisionerdaemons.go index da0d2b67d0910..c1b85345d0ba2 100644 --- a/codersdk/provisionerdaemons.go +++ b/codersdk/provisionerdaemons.go @@ -37,9 +37,9 @@ const ( ) type ProvisionerDaemon struct { - ID uuid.UUID `json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt sql.NullTime `json:"updated_at"` + ID uuid.UUID `json:"id" format:"uuid"` + CreatedAt time.Time `json:"created_at" format:"date-time"` + UpdatedAt sql.NullTime `json:"updated_at" format:"date-time"` Name string `json:"name"` Provisioners []ProvisionerType `json:"provisioners"` Tags map[string]string `json:"tags"` diff --git a/codersdk/replicas.go b/codersdk/replicas.go index e74af021ee9a3..ff44064db24ff 100644 --- a/codersdk/replicas.go +++ b/codersdk/replicas.go @@ -12,11 +12,11 @@ import ( type Replica struct { // ID is the unique identifier for the replica. - ID uuid.UUID `json:"id"` + ID uuid.UUID `json:"id" format:"uuid"` // Hostname is the hostname of the replica. Hostname string `json:"hostname"` // CreatedAt is when the replica was first seen. - CreatedAt time.Time `json:"created_at"` + CreatedAt time.Time `json:"created_at" format:"date-time"` // RelayAddress is the accessible address to relay DERP connections. RelayAddress string `json:"relay_address"` // RegionID is the region of the replica. diff --git a/codersdk/templates.go b/codersdk/templates.go index 2985f6e3aac15..4a8498d7f32b6 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -60,12 +60,12 @@ type TemplateACL struct { type TemplateGroup struct { Group - Role TemplateRole `json:"role"` + Role TemplateRole `json:"role" enums:"admin,use"` } type TemplateUser struct { User - Role TemplateRole `json:"role"` + Role TemplateRole `json:"role" enums:"admin,use"` } type UpdateTemplateACL struct { diff --git a/docs/api/enterprise.md b/docs/api/enterprise.md index b1cdfab2172ea..15ae38ede34e3 100644 --- a/docs/api/enterprise.md +++ b/docs/api/enterprise.md @@ -50,3 +50,672 @@ curl -X GET http://coder-server:8080/api/v2/entitlements \ | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Entitlements](schemas.md#codersdkentitlements) | To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get groups + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/groups \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /groups` + +### Parameters + +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------ | -------- | --------------- | +| `organization` | path | string(uuid) | true | Organization ID | + +### Example responses + +> 200 Response + +```json +[ + { + "avatar_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "members": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + } + ], + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "quota_allowance": 0 + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Group](schemas.md#codersdkgroup) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| --------------------- | ----------------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» avatar_url` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» members` | array | false | | | +| `»» avatar_url` | string(uri) | false | | | +| `»» created_at` | string(date-time) | true | | | +| `»» email` | string(email) | true | | | +| `»» id` | string(uuid) | true | | | +| `»» last_seen_at` | string(date-time) | false | | | +| `»» organization_ids` | array | false | | | +| `»» roles` | array | false | | | +| `»»» display_name` | string | false | | | +| `»»» name` | string | false | | | +| `»» status` | string | false | | | +| `»» username` | string | true | | | +| `» name` | string | false | | | +| `» organization_id` | string(uuid) | false | | | +| `» quota_allowance` | integer | false | | | + +#### Enumerated Values + +| Property | Value | +| -------- | ----------- | +| `status` | `active` | +| `status` | `suspended` | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get group by name + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/groups/{groupName} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /groups/{groupName}` + +### Parameters + +| Name | In | Type | Required | Description | +| ----------- | ---- | ------ | -------- | ----------- | +| `groupName` | path | string | true | Group name | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "members": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + } + ], + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "quota_allowance": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Group](schemas.md#codersdkgroup) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Delete license + +### Code samples + +```shell +# Example request using curl +curl -X DELETE http://coder-server:8080/api/v2/license/{id} \ + -H 'Coder-Session-Token: API_KEY' +``` + +`DELETE /license/{id}` + +### Parameters + +| Name | In | Type | Required | Description | +| ---- | ---- | -------------- | -------- | ----------- | +| `id` | path | string(number) | true | License ID | + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get licenses + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/licenses \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /licenses` + +### Example responses + +> 200 Response + +```json +[ + { + "claims": {}, + "id": 0, + "uploaded_at": "2019-08-24T14:15:22Z", + "uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.License](schemas.md#codersdklicense) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| --------------- | ----------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» claims` | object | false | | Claims are the JWT claims asserted by the license. Here we use
a generic string map to ensure that all data from the server is
parsed verbatim, not just the fields this version of Coder
understands. | +| `» id` | integer | false | | | +| `» uploaded_at` | string(date-time) | false | | | +| `» uuid` | string(uuid) | false | | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get groups by organization + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /organizations/{organization}/groups` + +### Parameters + +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------ | -------- | --------------- | +| `organization` | path | string(uuid) | true | Organization ID | + +### Example responses + +> 200 Response + +```json +[ + { + "avatar_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "members": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + } + ], + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "quota_allowance": 0 + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Group](schemas.md#codersdkgroup) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| --------------------- | ----------------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» avatar_url` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» members` | array | false | | | +| `»» avatar_url` | string(uri) | false | | | +| `»» created_at` | string(date-time) | true | | | +| `»» email` | string(email) | true | | | +| `»» id` | string(uuid) | true | | | +| `»» last_seen_at` | string(date-time) | false | | | +| `»» organization_ids` | array | false | | | +| `»» roles` | array | false | | | +| `»»» display_name` | string | false | | | +| `»»» name` | string | false | | | +| `»» status` | string | false | | | +| `»» username` | string | true | | | +| `» name` | string | false | | | +| `» organization_id` | string(uuid) | false | | | +| `» quota_allowance` | integer | false | | | + +#### Enumerated Values + +| Property | Value | +| -------- | ----------- | +| `status` | `active` | +| `status` | `suspended` | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get group by organization and name + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups/{groupName} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /organizations/{organization}/groups/{groupName}` + +### Parameters + +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------ | -------- | --------------- | +| `organization` | path | string(uuid) | true | Organization ID | +| `groupName` | path | string | true | Group name | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "members": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + } + ], + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "quota_allowance": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Group](schemas.md#codersdkgroup) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get provisioner daemons + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisionerdaemons \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /organizations/{organization}/provisionerdaemons` + +### Parameters + +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------ | -------- | --------------- | +| `organization` | path | string(uuid) | true | Organization ID | + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "provisioners": ["string"], + "tags": { + "property1": "string", + "property2": "string" + }, + "updated_at": { + "time": "string", + "valid": true + } + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.ProvisionerDaemon](schemas.md#codersdkprovisionerdaemon) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| ------------------- | ----------------- | -------- | ------------ | --------------------------------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | false | | | +| `» id` | string(uuid) | false | | | +| `» name` | string | false | | | +| `» provisioners` | array | false | | | +| `» tags` | object | false | | | +| `»» [any property]` | string | false | | | +| `» updated_at` | `sql.NullTime` | false | | | +| `»» time` | string | false | | | +| `»» valid` | boolean | false | | Valid is true if Time is not NULL | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Serve provisioner daemon + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisionerdaemons/serve \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /organizations/{organization}/provisionerdaemons/serve` + +### Parameters + +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------ | -------- | --------------- | +| `organization` | path | string(uuid) | true | Organization ID | + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------------------ | ------------------- | ------ | +| 101 | [Switching Protocols](https://tools.ietf.org/html/rfc7231#section-6.2.2) | Switching Protocols | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get active replicas + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/replicas \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /replicas` + +### Example responses + +> 200 Response + +```json +[ + { + "created_at": "2019-08-24T14:15:22Z", + "database_latency": 0, + "error": "string", + "hostname": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "region_id": 0, + "relay_address": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Replica](schemas.md#codersdkreplica) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| -------------------- | ----------------- | -------- | ------------ | ----------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | false | | CreatedAt is when the replica was first seen. | +| `» database_latency` | integer | false | | DatabaseLatency is the latency in microseconds to the database. | +| `» error` | string | false | | Error is the error. | +| `» hostname` | string | false | | Hostname is the hostname of the replica. | +| `» id` | string(uuid) | false | | ID is the unique identifier for the replica. | +| `» region_id` | integer | false | | RegionID is the region of the replica. | +| `» relay_address` | string | false | | RelayAddress is the accessible address to relay DERP connections. | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get template ACLs + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templates/{template}/acl \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templates/{template}/acl` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------- | ---- | ------------ | -------- | ----------- | +| `template` | path | string(uuid) | true | Template ID | + +### Example responses + +> 200 Response + +```json +[ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "role": "admin", + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.TemplateUser](schemas.md#codersdktemplateuser) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| -------------------- | ----------------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» avatar_url` | string(uri) | false | | | +| `» created_at` | string(date-time) | true | | | +| `» email` | string(email) | true | | | +| `» id` | string(uuid) | true | | | +| `» last_seen_at` | string(date-time) | false | | | +| `» organization_ids` | array | false | | | +| `» role` | string | false | | | +| `» roles` | array | false | | | +| `»» display_name` | string | false | | | +| `»» name` | string | false | | | +| `» status` | string | false | | | +| `» username` | string | true | | | + +#### Enumerated Values + +| Property | Value | +| -------- | ----------- | +| `role` | `admin` | +| `role` | `use` | +| `status` | `active` | +| `status` | `suspended` | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update template ACL + +### Code samples + +```shell +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/templates/{template}/acl \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /templates/{template}/acl` + +> Body parameter + +```json +{ + "group_perms": { + "property1": "admin", + "property2": "admin" + }, + "user_perms": { + "property1": "admin", + "property2": "admin" + } +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------- | ---- | ------------------------------------------------------------------ | -------- | ----------------------- | +| `template` | path | string(uuid) | true | Template ID | +| `body` | body | [codersdk.UpdateTemplateACL](schemas.md#codersdkupdatetemplateacl) | true | Update template request | + +### Example responses + +> 200 Response + +```json +{ + "detail": "string", + "message": "string", + "validations": [ + { + "detail": "string", + "field": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/docs/api/organizations.md b/docs/api/organizations.md index 18d4d70192efa..825f76f8a98ce 100644 --- a/docs/api/organizations.md +++ b/docs/api/organizations.md @@ -2,6 +2,55 @@ > This page is incomplete, stay tuned. +## Add new license + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/licenses \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /licenses` + +> Body parameter + +```json +{ + "license": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------------------------------------------------------------------ | -------- | ------------------- | +| `body` | body | [codersdk.AddLicenseRequest](schemas.md#codersdkaddlicenserequest) | true | Add license request | + +### Example responses + +> 201 Response + +```json +{ + "claims": {}, + "id": 0, + "uploaded_at": "2019-08-24T14:15:22Z", + "uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------ | ----------- | ---------------------------------------------- | +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.License](schemas.md#codersdklicense) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Create organization ### Code samples diff --git a/docs/api/schemas.md b/docs/api/schemas.md index c6eb8b47c2bce..20867e9659eae 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -73,6 +73,20 @@ | `document` | string | true | | | | `signature` | string | true | | | +## codersdk.AddLicenseRequest + +```json +{ + "license": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| --------- | ------ | -------- | ------------ | ----------- | +| `license` | string | true | | | + ## codersdk.AgentGitSSHKey ```json @@ -516,6 +530,24 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `organization_id` | string | false | | | | `user_id` | string | false | | | +## codersdk.CreateGroupRequest + +```json +{ + "avatar_url": "string", + "name": "string", + "quota_allowance": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------------- | ------- | -------- | ------------ | ----------- | +| `avatar_url` | string | false | | | +| `name` | string | false | | | +| `quota_allowance` | integer | false | | | + ## codersdk.CreateOrganizationRequest ```json @@ -2265,6 +2297,47 @@ CreateParameterRequest is a structure used to create a new parameter value for a | ---------------- | ------ | -------- | ------------ | ----------- | | `json_web_token` | string | true | | | +## codersdk.Group + +```json +{ + "avatar_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "members": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + } + ], + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "quota_allowance": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ----------------- | --------------------------------------- | -------- | ------------ | ----------- | +| `avatar_url` | string | false | | | +| `id` | string | false | | | +| `members` | array of [codersdk.User](#codersdkuser) | false | | | +| `name` | string | false | | | +| `organization_id` | string | false | | | +| `quota_allowance` | integer | false | | | + ## codersdk.Healthcheck ```json @@ -2283,6 +2356,26 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | | `url` | string | false | | URL specifies the endpoint to check for the app health. | +## codersdk.License + +```json +{ + "claims": {}, + "id": 0, + "uploaded_at": "2019-08-24T14:15:22Z", + "uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `claims` | object | false | | Claims are the JWT claims asserted by the license. Here we use a generic string map to ensure that all data from the server is parsed verbatim, not just the fields this version of Coder understands. | +| `id` | integer | false | | | +| `uploaded_at` | string | false | | | +| `uuid` | string | false | | | + ## codersdk.LoginWithPasswordRequest ```json @@ -2905,6 +2998,37 @@ Parameter represents a set value for the scope. | `daemons` | [codersdk.DeploymentConfigField-int](#codersdkdeploymentconfigfield-int) | false | | | | `force_cancel_interval` | [codersdk.DeploymentConfigField-time_Duration](#codersdkdeploymentconfigfield-time_duration) | false | | | +## codersdk.ProvisionerDaemon + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "provisioners": ["string"], + "tags": { + "property1": "string", + "property2": "string" + }, + "updated_at": { + "time": "string", + "valid": true + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | ---------------------------- | -------- | ------------ | ----------- | +| `created_at` | string | false | | | +| `id` | string | false | | | +| `name` | string | false | | | +| `provisioners` | array of string | false | | | +| `tags` | object | false | | | +| » `[any property]` | string | false | | | +| `updated_at` | [sql.NullTime](#sqlnulltime) | false | | | + ## codersdk.ProvisionerJob ```json @@ -3000,6 +3124,32 @@ Parameter represents a set value for the scope. | ---------- | ------ | -------- | ------------ | ----------- | | `deadline` | string | true | | | +## codersdk.Replica + +```json +{ + "created_at": "2019-08-24T14:15:22Z", + "database_latency": 0, + "error": "string", + "hostname": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "region_id": 0, + "relay_address": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | ------- | -------- | ------------ | ------------------------------------------------------------------ | +| `created_at` | string | false | | Created at is when the replica was first seen. | +| `database_latency` | integer | false | | Database latency is the latency in microseconds to the database. | +| `error` | string | false | | Error is the error. | +| `hostname` | string | false | | Hostname is the hostname of the replica. | +| `id` | string | false | | ID is the unique identifier for the replica. | +| `region_id` | integer | false | | Region ID is the region of the replica. | +| `relay_address` | string | false | | Relay address is the accessible address to relay DERP connections. | + ## codersdk.Response ```json @@ -3364,6 +3514,52 @@ Parameter represents a set value for the scope. | `tags` | array of string | false | | | | `url` | string | false | | | +## codersdk.TemplateUser + +```json +{ + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "role": "admin", + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | --------------------------------------- | -------- | ------------ | ----------- | +| `avatar_url` | string | false | | | +| `created_at` | string | true | | | +| `email` | string | true | | | +| `id` | string | true | | | +| `last_seen_at` | string | false | | | +| `organization_ids` | array of string | false | | | +| `role` | string | false | | | +| `roles` | array of [codersdk.Role](#codersdkrole) | false | | | +| `status` | string | false | | | +| `username` | string | true | | | + +#### Enumerated Values + +| Property | Value | +| -------- | ----------- | +| `role` | `admin` | +| `role` | `use` | +| `status` | `active` | +| `status` | `suspended` | + ## codersdk.TemplateVersion ```json @@ -3533,6 +3729,39 @@ Parameter represents a set value for the scope. | ------- | --------------- | -------- | ------------ | ----------- | | `roles` | array of string | false | | | +## codersdk.UpdateTemplateACL + +```json +{ + "group_perms": { + "property1": "admin", + "property2": "admin" + }, + "user_perms": { + "property1": "admin", + "property2": "admin" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | ------ | -------- | ------------ | ----------- | +| `group_perms` | object | false | | | +| » `[any property]` | string | false | | | +| `user_perms` | object | false | | | +| » `[any property]` | string | false | | | + +#### Enumerated Values + +| Property | Value | +| -------------------------- | ------- | +| `**additionalProperties**` | `admin` | +| `**additionalProperties**` | `use` | +| `**additionalProperties**` | `admin` | +| `**additionalProperties**` | `use` | + ## codersdk.UpdateUserPasswordRequest ```json @@ -4558,6 +4787,22 @@ _None_ | `source_value` | string | false | | | | `updated_at` | string | false | | | +## sql.NullTime + +```json +{ + "time": "string", + "valid": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------- | ------- | -------- | ------------ | --------------------------------- | +| `time` | string | false | | | +| `valid` | boolean | false | | Valid is true if Time is not NULL | + ## tailcfg.DERPMap ```json diff --git a/docs/api/templates.md b/docs/api/templates.md index 749836cc8789a..ca64657f93beb 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -2,6 +2,77 @@ > This page is incomplete, stay tuned. +## Create group for organization + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/groups \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /organizations/{organization}/groups` + +> Body parameter + +```json +{ + "avatar_url": "string", + "name": "string", + "quota_allowance": 0 +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| -------------- | ---- | -------------------------------------------------------------------- | -------- | -------------------- | +| `organization` | path | string | true | Organization ID | +| `body` | body | [codersdk.CreateGroupRequest](schemas.md#codersdkcreategrouprequest) | true | Create group request | + +### Example responses + +> 201 Response + +```json +{ + "avatar_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "members": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + } + ], + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "quota_allowance": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------ | ----------- | ------------------------------------------ | +| 201 | [Created](https://tools.ietf.org/html/rfc7231#section-6.3.2) | Created | [codersdk.Group](schemas.md#codersdkgroup) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Get templates by organization ### Code samples diff --git a/docs/manifest.json b/docs/manifest.json index 02d7f32110433..ec3ac821b659f 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -357,6 +357,10 @@ "title": "Organizations", "path": "./api/organizations.md" }, + { + "title": "Templates", + "path": "./api/templates.md" + }, { "title": "Members", "path": "./api/members.md" @@ -365,10 +369,6 @@ "title": "Workspaces", "path": "./api/workspaces.md" }, - { - "title": "Templates", - "path": "./api/templates.md" - }, { "title": "Parameters", "path": "./api/parameters.md" diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index afb3c4c64f816..32184e43cdac1 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -81,7 +81,7 @@ func New(ctx context.Context, options *Options) (*API, error) { httpmw.ExtractOrganizationParam(api.Database), ) r.Post("/", api.postGroupByOrganization) - r.Get("/", api.groups) + r.Get("/", api.groupsByOrganization) r.Route("/{groupName}", func(r chi.Router) { r.Use( httpmw.ExtractGroupByNameParam(api.Database), diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index ec566c8569745..9139867ffdafa 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -17,6 +17,16 @@ import ( "github.com/coder/coder/codersdk" ) +// @Summary Create group for organization +// @ID create-group-for-organization +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Templates +// @Param request body codersdk.CreateGroupRequest true "Create group request" +// @Param organization path string true "Organization ID" +// @Success 201 {object} codersdk.Group +// @Router /organizations/{organization}/groups [post] func (api *API) postGroupByOrganization(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -264,6 +274,27 @@ func (api *API) deleteGroup(rw http.ResponseWriter, r *http.Request) { }) } +// @Summary Get group by organization and name +// @ID get-group-by-organization-and-name +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param organization path string true "Organization ID" format(uuid) +// @Param groupName path string true "Group name" +// @Success 200 {object} codersdk.Group +// @Router /organizations/{organization}/groups/{groupName} [get] +func (api *API) groupByOrganization(rw http.ResponseWriter, r *http.Request) { + api.group(rw, r) +} + +// @Summary Get group by name +// @ID get-group-by-name +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param groupName path string true "Group name" +// @Success 200 {object} codersdk.Group +// @Router /groups/{groupName} [get] func (api *API) group(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -284,6 +315,26 @@ func (api *API) group(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, convertGroup(group, users)) } +// @Summary Get groups by organization +// @ID get-groups-by-organization +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param organization path string true "Organization ID" format(uuid) +// @Success 200 {array} codersdk.Group +// @Router /organizations/{organization}/groups [get] +func (api *API) groupsByOrganization(rw http.ResponseWriter, r *http.Request) { + api.groups(rw, r) +} + +// @Summary Get groups +// @ID get-groups +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param organization path string true "Organization ID" format(uuid) +// @Success 200 {array} codersdk.Group +// @Router /groups [get] func (api *API) groups(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/enterprise/coderd/licenses.go b/enterprise/coderd/licenses.go index 28a732e2b88db..0ef3a564f9571 100644 --- a/enterprise/coderd/licenses.go +++ b/enterprise/coderd/licenses.go @@ -48,6 +48,15 @@ var Keys = map[string]ed25519.PublicKey{"2022-08-12": ed25519.PublicKey(key20220 // we generally don't want the old features to immediately break without warning. With a grace // period on the license, features will continue to work from the old license until its grace // period, then the users will get a warning allowing them to gracefully stop using the feature. +// +// @Summary Add new license +// @ID add-new-license +// @Security CoderSessionToken +// @Produce json +// @Tags Organizations +// @Param request body codersdk.AddLicenseRequest true "Add license request" +// @Success 201 {object} codersdk.License +// @Router /licenses [post] func (api *API) postLicense(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() if !api.AGPL.Authorize(r, rbac.ActionCreate, rbac.ResourceLicense) { @@ -121,6 +130,13 @@ func (api *API) postLicense(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusCreated, convertLicense(dl, rawClaims)) } +// @Summary Get licenses +// @ID get-licenses +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Success 200 {array} codersdk.License +// @Router /licenses [get] func (api *API) licenses(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() licenses, err := api.Database.GetLicenses(ctx) @@ -155,6 +171,14 @@ func (api *API) licenses(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, sdkLicenses) } +// @Summary Delete license +// @ID delete-license +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param id path string true "License ID" format(number) +// @Success 200 +// @Router /license/{id} [delete] func (api *API) deleteLicense(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() if !api.AGPL.Authorize(r, rbac.ActionDelete, rbac.ResourceLicense) { diff --git a/enterprise/coderd/provisionerdaemons.go b/enterprise/coderd/provisionerdaemons.go index d13c4ffdd1361..d49a96ab139d6 100644 --- a/enterprise/coderd/provisionerdaemons.go +++ b/enterprise/coderd/provisionerdaemons.go @@ -45,6 +45,14 @@ func (api *API) provisionerDaemonsEnabledMW(next http.Handler) http.Handler { }) } +// @Summary Get provisioner daemons +// @ID get-provisioner-daemons +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param organization path string true "Organization ID" format(uuid) +// @Success 200 {array} codersdk.ProvisionerDaemon +// @Router /organizations/{organization}/provisionerdaemons [get] func (api *API) provisionerDaemons(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() org := httpmw.OrganizationParam(r) @@ -82,6 +90,15 @@ func (api *API) provisionerDaemons(rw http.ResponseWriter, r *http.Request) { } // Serves the provisioner daemon protobuf API over a WebSocket. +// +// @Summary Serve provisioner daemon +// @ID serve-provisioner-daemon +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param organization path string true "Organization ID" format(uuid) +// @Success 101 +// @Router /organizations/{organization}/provisionerdaemons/serve [get] func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request) { tags := map[string]string{} if r.URL.Query().Has("tag") { diff --git a/enterprise/coderd/replicas.go b/enterprise/coderd/replicas.go index 906597f257f04..b5c5af2743385 100644 --- a/enterprise/coderd/replicas.go +++ b/enterprise/coderd/replicas.go @@ -10,6 +10,14 @@ import ( ) // replicas returns the number of replicas that are active in Coder. +// +// @Summary Get active replicas +// @ID get-active-replicas +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Success 200 {array} codersdk.Replica +// @Router /replicas [get] func (api *API) replicas(rw http.ResponseWriter, r *http.Request) { if !api.AGPL.Authorize(r, rbac.ActionRead, rbac.ResourceReplicas) { httpapi.ResourceNotFound(rw) diff --git a/enterprise/coderd/templates.go b/enterprise/coderd/templates.go index 0e11261710f00..397101c51fb1c 100644 --- a/enterprise/coderd/templates.go +++ b/enterprise/coderd/templates.go @@ -18,6 +18,14 @@ import ( "github.com/coder/coder/codersdk" ) +// @Summary Get template ACLs +// @ID get-template-acls +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param template path string true "Template ID" format(uuid) +// @Success 200 {array} codersdk.TemplateUser +// @Router /templates/{template}/acl [get] func (api *API) templateACL(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -92,6 +100,15 @@ func (api *API) templateACL(rw http.ResponseWriter, r *http.Request) { }) } +// @Summary Update template ACL +// @ID update-template-acl +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param template path string true "Template ID" format(uuid) +// @Param request body codersdk.UpdateTemplateACL true "Update template request" +// @Success 200 {object} codersdk.Response +// @Router /templates/{template}/acl [patch] func (api *API) patchTemplateACL(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() From 35eae4e4ae9b5a084aaf4a60c253319a1e384a7a Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 15:15:53 +0100 Subject: [PATCH 37/61] SCIM --- coderd/apidoc/docs.go | 345 ++++++++++++++++++++++--- coderd/apidoc/swagger.json | 317 ++++++++++++++++++++--- docs/api/enterprise.md | 379 +++++++++++++++++++++++----- docs/api/schemas.md | 101 ++++++++ docs/manifest.json | 8 +- enterprise/coderd/appearance.go | 15 ++ enterprise/coderd/groups.go | 13 - enterprise/coderd/scim.go | 42 ++- enterprise/coderd/scim_test.go | 2 +- enterprise/coderd/workspacequota.go | 8 + 10 files changed, 1071 insertions(+), 159 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index beaedc3060963..dcdab5ce2b602 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -45,6 +45,65 @@ const docTemplate = `{ } } }, + "/appearance": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get appearance", + "operationId": "get-appearance", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AppearanceConfig" + } + } + } + }, + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Update appearance", + "operationId": "update-appearance", + "parameters": [ + { + "description": "Update appearance request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AppearanceConfig" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AppearanceConfig" + } + } + } + } + }, "/applications/auth-redirect": { "get": { "security": [ @@ -722,48 +781,6 @@ const docTemplate = `{ } } }, - "/organizations/{organization}/groups/{groupName}": { - "get": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "Enterprise" - ], - "summary": "Get group by organization and name", - "operationId": "get-group-by-organization-and-name", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Group name", - "name": "groupName", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Group" - } - } - } - } - }, "/organizations/{organization}/members/roles": { "get": { "security": [ @@ -1449,6 +1466,116 @@ const docTemplate = `{ } } }, + "/scim/v2/Users": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "SCIM 2.0: Create new user", + "operationId": "scim-create-new-user", + "parameters": [ + { + "description": "New user", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/coderd.SCIMUser" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/coderd.SCIMUser" + } + } + } + } + }, + "/scim/v2/Users/{id}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/scim+json" + ], + "tags": [ + "Enterprise" + ], + "summary": "SCIM 2.0: Get user by ID", + "operationId": "scim-get-user-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "404": { + "description": "Not Found" + } + } + }, + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/scim+json" + ], + "tags": [ + "Enterprise" + ], + "summary": "SCIM 2.0: Update user account", + "operationId": "scim-update-user-status", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Update user request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/coderd.SCIMUser" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, "/templates/{id}": { "get": { "security": [ @@ -3345,6 +3472,40 @@ const docTemplate = `{ } } }, + "/workspace-quota/{user}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get workspace quota by user", + "operationId": "get-workspace-quota-by-user", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceQuota" + } + } + } + } + }, "/workspaceagents/aws-instance-identity": { "post": { "security": [ @@ -4312,6 +4473,70 @@ const docTemplate = `{ } }, "definitions": { + "coderd.SCIMUser": { + "type": "object", + "properties": { + "active": { + "type": "boolean" + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "display": { + "type": "string" + }, + "primary": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "value": { + "type": "string", + "format": "email" + } + } + } + }, + "groups": { + "type": "array", + "items": {} + }, + "id": { + "type": "string" + }, + "meta": { + "type": "object", + "properties": { + "resourceType": { + "type": "string" + } + } + }, + "name": { + "type": "object", + "properties": { + "familyName": { + "type": "string" + }, + "givenName": { + "type": "string" + } + } + }, + "schemas": { + "type": "array", + "items": { + "type": "string" + } + }, + "userName": { + "type": "string" + } + } + }, "coderd.cspViolation": { "type": "object", "properties": { @@ -4458,6 +4683,17 @@ const docTemplate = `{ } } }, + "codersdk.AppearanceConfig": { + "type": "object", + "properties": { + "logo_url": { + "type": "string" + }, + "service_banner": { + "$ref": "#/definitions/codersdk.ServiceBannerConfig" + } + } + }, "codersdk.AssignableRoles": { "type": "object", "properties": { @@ -6033,6 +6269,20 @@ const docTemplate = `{ } } }, + "codersdk.ServiceBannerConfig": { + "type": "object", + "properties": { + "background_color": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "message": { + "type": "string" + } + } + }, "codersdk.SwaggerConfig": { "type": "object", "properties": { @@ -6868,6 +7118,17 @@ const docTemplate = `{ } } }, + "codersdk.WorkspaceQuota": { + "type": "object", + "properties": { + "budget": { + "type": "integer" + }, + "credits_consumed": { + "type": "integer" + } + } + }, "codersdk.WorkspaceResource": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 417e6d47b2ec1..7bdeba0b2471a 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -33,6 +33,57 @@ } } }, + "/appearance": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get appearance", + "operationId": "get-appearance", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AppearanceConfig" + } + } + } + }, + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Update appearance", + "operationId": "update-appearance", + "parameters": [ + { + "description": "Update appearance request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.AppearanceConfig" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.AppearanceConfig" + } + } + } + } + }, "/applications/auth-redirect": { "get": { "security": [ @@ -626,44 +677,6 @@ } } }, - "/organizations/{organization}/groups/{groupName}": { - "get": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get group by organization and name", - "operationId": "get-group-by-organization-and-name", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Organization ID", - "name": "organization", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Group name", - "name": "groupName", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Group" - } - } - } - } - }, "/organizations/{organization}/members/roles": { "get": { "security": [ @@ -1265,6 +1278,104 @@ } } }, + "/scim/v2/Users": { + "post": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "SCIM 2.0: Create new user", + "operationId": "scim-create-new-user", + "parameters": [ + { + "description": "New user", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/coderd.SCIMUser" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/coderd.SCIMUser" + } + } + } + } + }, + "/scim/v2/Users/{id}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/scim+json"], + "tags": ["Enterprise"], + "summary": "SCIM 2.0: Get user by ID", + "operationId": "scim-get-user-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "404": { + "description": "Not Found" + } + } + }, + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/scim+json"], + "tags": ["Enterprise"], + "summary": "SCIM 2.0: Update user account", + "operationId": "scim-update-user-status", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Update user request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/coderd.SCIMUser" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.User" + } + } + } + } + }, "/templates/{id}": { "get": { "security": [ @@ -2949,6 +3060,36 @@ } } }, + "/workspace-quota/{user}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get workspace quota by user", + "operationId": "get-workspace-quota-by-user", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceQuota" + } + } + } + } + }, "/workspaceagents/aws-instance-identity": { "post": { "security": [ @@ -3801,6 +3942,70 @@ } }, "definitions": { + "coderd.SCIMUser": { + "type": "object", + "properties": { + "active": { + "type": "boolean" + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "display": { + "type": "string" + }, + "primary": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "value": { + "type": "string", + "format": "email" + } + } + } + }, + "groups": { + "type": "array", + "items": {} + }, + "id": { + "type": "string" + }, + "meta": { + "type": "object", + "properties": { + "resourceType": { + "type": "string" + } + } + }, + "name": { + "type": "object", + "properties": { + "familyName": { + "type": "string" + }, + "givenName": { + "type": "string" + } + } + }, + "schemas": { + "type": "array", + "items": { + "type": "string" + } + }, + "userName": { + "type": "string" + } + } + }, "coderd.cspViolation": { "type": "object", "properties": { @@ -3934,6 +4139,17 @@ } } }, + "codersdk.AppearanceConfig": { + "type": "object", + "properties": { + "logo_url": { + "type": "string" + }, + "service_banner": { + "$ref": "#/definitions/codersdk.ServiceBannerConfig" + } + } + }, "codersdk.AssignableRoles": { "type": "object", "properties": { @@ -5426,6 +5642,20 @@ } } }, + "codersdk.ServiceBannerConfig": { + "type": "object", + "properties": { + "background_color": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "message": { + "type": "string" + } + } + }, "codersdk.SwaggerConfig": { "type": "object", "properties": { @@ -6210,6 +6440,17 @@ } } }, + "codersdk.WorkspaceQuota": { + "type": "object", + "properties": { + "budget": { + "type": "integer" + }, + "credits_consumed": { + "type": "integer" + } + } + }, "codersdk.WorkspaceResource": { "type": "object", "properties": { diff --git a/docs/api/enterprise.md b/docs/api/enterprise.md index 15ae38ede34e3..d3b645d7cba76 100644 --- a/docs/api/enterprise.md +++ b/docs/api/enterprise.md @@ -2,6 +2,98 @@ > This page is incomplete, stay tuned. +## Get appearance + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/appearance \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /appearance` + +### Example responses + +> 200 Response + +```json +{ + "logo_url": "string", + "service_banner": { + "background_color": "string", + "enabled": true, + "message": "string" + } +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.AppearanceConfig](schemas.md#codersdkappearanceconfig) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update appearance + +### Code samples + +```shell +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/appearance \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /appearance` + +> Body parameter + +```json +{ + "logo_url": "string", + "service_banner": { + "background_color": "string", + "enabled": true, + "message": "string" + } +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ---------------------------------------------------------------- | -------- | ------------------------- | +| `body` | body | [codersdk.AppearanceConfig](schemas.md#codersdkappearanceconfig) | true | Update appearance request | + +### Example responses + +> 200 Response + +```json +{ + "logo_url": "string", + "service_banner": { + "background_color": "string", + "enabled": true, + "message": "string" + } +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.AppearanceConfig](schemas.md#codersdkappearanceconfig) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Get entitlements ### Code samples @@ -370,66 +462,6 @@ Status Code **200** To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Get group by organization and name - -### Code samples - -```shell -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups/{groupName} \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /organizations/{organization}/groups/{groupName}` - -### Parameters - -| Name | In | Type | Required | Description | -| -------------- | ---- | ------------ | -------- | --------------- | -| `organization` | path | string(uuid) | true | Organization ID | -| `groupName` | path | string | true | Group name | - -### Example responses - -> 200 Response - -```json -{ - "avatar_url": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "members": [ - { - "avatar_url": "http://example.com", - "created_at": "2019-08-24T14:15:22Z", - "email": "user@example.com", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "last_seen_at": "2019-08-24T14:15:22Z", - "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], - "roles": [ - { - "display_name": "string", - "name": "string" - } - ], - "status": "active", - "username": "string" - } - ], - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "quota_allowance": 0 -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------ | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Group](schemas.md#codersdkgroup) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - ## Get provisioner daemons ### Code samples @@ -577,6 +609,195 @@ Status Code **200** To perform this operation, you must be authenticated. [Learn more](authentication.md). +## SCIM 2.0: Create new user + +### Code samples + +```shell +# Example request using curl +curl -X POST http://coder-server:8080/api/v2/scim/v2/Users \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`POST /scim/v2/Users` + +> Body parameter + +```json +{ + "active": true, + "emails": [ + { + "display": "string", + "primary": true, + "type": "string", + "value": "user@example.com" + } + ], + "groups": [null], + "id": "string", + "meta": { + "resourceType": "string" + }, + "name": { + "familyName": "string", + "givenName": "string" + }, + "schemas": ["string"], + "userName": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | -------------------------------------------- | -------- | ----------- | +| `body` | body | [coderd.SCIMUser](schemas.md#coderdscimuser) | true | New user | + +### Example responses + +> 200 Response + +```json +{ + "active": true, + "emails": [ + { + "display": "string", + "primary": true, + "type": "string", + "value": "user@example.com" + } + ], + "groups": [null], + "id": "string", + "meta": { + "resourceType": "string" + }, + "name": { + "familyName": "string", + "givenName": "string" + }, + "schemas": ["string"], + "userName": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [coderd.SCIMUser](schemas.md#coderdscimuser) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## SCIM 2.0: Get user by ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/scim/v2/Users/{id} \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /scim/v2/Users/{id}` + +### Parameters + +| Name | In | Type | Required | Description | +| ---- | ---- | ------------ | -------- | ----------- | +| `id` | path | string(uuid) | true | User ID | + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | -------------------------------------------------------------- | ----------- | ------ | +| 404 | [Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4) | Not Found | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## SCIM 2.0: Update user account + +### Code samples + +```shell +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/scim/v2/Users/{id} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/scim+json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /scim/v2/Users/{id}` + +> Body parameter + +```json +{ + "active": true, + "emails": [ + { + "display": "string", + "primary": true, + "type": "string", + "value": "user@example.com" + } + ], + "groups": [null], + "id": "string", + "meta": { + "resourceType": "string" + }, + "name": { + "familyName": "string", + "givenName": "string" + }, + "schemas": ["string"], + "userName": "string" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | -------------------------------------------- | -------- | ------------------- | +| `id` | path | string(uuid) | true | User ID | +| `body` | body | [coderd.SCIMUser](schemas.md#coderdscimuser) | true | Update user request | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.User](schemas.md#codersdkuser) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Get template ACLs ### Code samples @@ -719,3 +940,41 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template}/acl \ | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get workspace quota by user + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspace-quota/{user} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspace-quota/{user}` + +### Parameters + +| Name | In | Type | Required | Description | +| ------ | ---- | ------ | -------- | -------------------- | +| `user` | path | string | true | User ID, name, or me | + +### Example responses + +> 200 Response + +```json +{ + "budget": 0, + "credits_consumed": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceQuota](schemas.md#codersdkworkspacequota) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 20867e9659eae..109cc2f8aa49e 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2,6 +2,53 @@ > This page is incomplete, stay tuned. +## coderd.SCIMUser + +```json +{ + "active": true, + "emails": [ + { + "display": "string", + "primary": true, + "type": "string", + "value": "user@example.com" + } + ], + "groups": [null], + "id": "string", + "meta": { + "resourceType": "string" + }, + "name": { + "familyName": "string", + "givenName": "string" + }, + "schemas": ["string"], + "userName": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------------- | ------------------ | -------- | ------------ | ----------- | +| `active` | boolean | false | | | +| `emails` | array of object | false | | | +| `» display` | string | false | | | +| `» primary` | boolean | false | | | +| `» type` | string | false | | | +| `» value` | string | false | | | +| `groups` | array of undefined | false | | | +| `id` | string | false | | | +| `meta` | object | false | | | +| `» resourceType` | string | false | | | +| `name` | object | false | | | +| `» familyName` | string | false | | | +| `» givenName` | string | false | | | +| `schemas` | array of string | false | | | +| `userName` | string | false | | | + ## coderd.cspViolation ```json @@ -145,6 +192,26 @@ | ----------------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------ | | `report_interval` | integer | false | | Report interval is the duration after which the agent should send stats again. | +## codersdk.AppearanceConfig + +```json +{ + "logo_url": "string", + "service_banner": { + "background_color": "string", + "enabled": true, + "message": "string" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------------- | ------------------------------------------------------------ | -------- | ------------ | ----------- | +| `logo_url` | string | false | | | +| `service_banner` | [codersdk.ServiceBannerConfig](#codersdkservicebannerconfig) | false | | | + ## codersdk.AssignableRoles ```json @@ -3189,6 +3256,24 @@ Parameter represents a set value for the scope. | `display_name` | string | false | | | | `name` | string | false | | | +## codersdk.ServiceBannerConfig + +```json +{ + "background_color": "string", + "enabled": true, + "message": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | ------- | -------- | ------------ | ----------- | +| `background_color` | string | false | | | +| `enabled` | boolean | false | | | +| `message` | string | false | | | + ## codersdk.SwaggerConfig ```json @@ -4486,6 +4571,22 @@ Parameter represents a set value for the scope. | `transition` | `stop` | | `transition` | `delete` | +## codersdk.WorkspaceQuota + +```json +{ + "budget": 0, + "credits_consumed": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | ------- | -------- | ------------ | ----------- | +| `budget` | integer | false | | | +| `credits_consumed` | integer | false | | | + ## codersdk.WorkspaceResource ```json diff --git a/docs/manifest.json b/docs/manifest.json index ec3ac821b659f..17406fc7ac353 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -333,6 +333,10 @@ "title": "General", "path": "./api/general.md" }, + { + "title": "Enterprise", + "path": "./api/enterprise.md" + }, { "title": "Applications", "path": "./api/applications.md" @@ -345,10 +349,6 @@ "title": "Authorization", "path": "./api/authorization.md" }, - { - "title": "Enterprise", - "path": "./api/enterprise.md" - }, { "title": "Files", "path": "./api/files.md" diff --git a/enterprise/coderd/appearance.go b/enterprise/coderd/appearance.go index ab7a9cde1b5fd..fef1c2ff5ec63 100644 --- a/enterprise/coderd/appearance.go +++ b/enterprise/coderd/appearance.go @@ -15,6 +15,13 @@ import ( "github.com/coder/coder/codersdk" ) +// @Summary Get appearance +// @ID get-appearance +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Success 200 {object} codersdk.AppearanceConfig +// @Router /appearance [get] func (api *API) appearance(rw http.ResponseWriter, r *http.Request) { api.entitlementsMu.RLock() isEntitled := api.entitlements.Features[codersdk.FeatureAppearance].Entitlement == codersdk.EntitlementEntitled @@ -74,6 +81,14 @@ func validateHexColor(color string) error { return err } +// @Summary Update appearance +// @ID update-appearance +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param request body codersdk.AppearanceConfig true "Update appearance request" +// @Success 200 {object} codersdk.AppearanceConfig +// @Router /appearance [put] func (api *API) putAppearance(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index 9139867ffdafa..ce0f92fc22339 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -274,19 +274,6 @@ func (api *API) deleteGroup(rw http.ResponseWriter, r *http.Request) { }) } -// @Summary Get group by organization and name -// @ID get-group-by-organization-and-name -// @Security CoderSessionToken -// @Produce json -// @Tags Enterprise -// @Param organization path string true "Organization ID" format(uuid) -// @Param groupName path string true "Group name" -// @Success 200 {object} codersdk.Group -// @Router /organizations/{organization}/groups/{groupName} [get] -func (api *API) groupByOrganization(rw http.ResponseWriter, r *http.Request) { - api.group(rw, r) -} - // @Summary Get group by name // @ID get-group-by-name // @Security CoderSessionToken diff --git a/enterprise/coderd/scim.go b/enterprise/coderd/scim.go index 7ee4a41a79e60..b72486fb28ea0 100644 --- a/enterprise/coderd/scim.go +++ b/enterprise/coderd/scim.go @@ -43,6 +43,14 @@ func (api *API) scimVerifyAuthHeader(r *http.Request) bool { // Okta to try and create each user individually, this way we don't need to // implement fetching users twice. // +// @Summary SCIM 2.0: Get users +// @ID scim-get-users +// @Security CoderSessionToken +// @Produce application/scim+json +// @Tags Enterprise +// @Success 200 +// @Router /scim/v2/Users [post] +// //nolint:revive func (api *API) scimGetUsers(rw http.ResponseWriter, r *http.Request) { if !api.scimVerifyAuthHeader(r) { @@ -62,6 +70,19 @@ func (api *API) scimGetUsers(rw http.ResponseWriter, r *http.Request) { // This is done to always force Okta to try and create the user, this way we // don't need to implement fetching users twice. // +// scimGetUsers intentionally always returns no users. This is done to always force +// Okta to try and create each user individually, this way we don't need to +// implement fetching users twice. +// +// @Summary SCIM 2.0: Get user by ID +// @ID scim-get-user-by-id +// @Security CoderSessionToken +// @Produce application/scim+json +// @Tags Enterprise +// @Param id path string true "User ID" format(uuid) +// @Failure 404 +// @Router /scim/v2/Users/{id} [get] +// //nolint:revive func (api *API) scimGetUser(rw http.ResponseWriter, r *http.Request) { if !api.scimVerifyAuthHeader(r) { @@ -86,7 +107,7 @@ type SCIMUser struct { } `json:"name"` Emails []struct { Primary bool `json:"primary"` - Value string `json:"value"` + Value string `json:"value" format:"email"` Type string `json:"type"` Display string `json:"display"` } `json:"emails"` @@ -98,6 +119,15 @@ type SCIMUser struct { } // scimPostUser creates a new user, or returns the existing user if it exists. +// +// @Summary SCIM 2.0: Create new user +// @ID scim-create-new-user +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param request body coderd.SCIMUser true "New user" +// @Success 200 {object} coderd.SCIMUser +// @Router /scim/v2/Users [post] func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() if !api.scimVerifyAuthHeader(r) { @@ -144,6 +174,16 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) { } // scimPatchUser supports suspending and activating users only. +// +// @Summary SCIM 2.0: Update user account +// @ID scim-update-user-status +// @Security CoderSessionToken +// @Produce application/scim+json +// @Tags Enterprise +// @Param id path string true "User ID" format(uuid) +// @Param request body coderd.SCIMUser true "Update user request" +// @Success 200 {object} codersdk.User +// @Router /scim/v2/Users/{id} [patch] func (api *API) scimPatchUser(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() if !api.scimVerifyAuthHeader(r) { diff --git a/enterprise/coderd/scim_test.go b/enterprise/coderd/scim_test.go index 79fd0c533ad0e..c0ebf1356d120 100644 --- a/enterprise/coderd/scim_test.go +++ b/enterprise/coderd/scim_test.go @@ -34,7 +34,7 @@ func makeScimUser(t testing.TB) coderd.SCIMUser { }, Emails: []struct { Primary bool "json:\"primary\"" - Value string "json:\"value\"" + Value string "json:\"value\" format:\"email\"" Type string "json:\"type\"" Display string "json:\"display\"" }{ diff --git a/enterprise/coderd/workspacequota.go b/enterprise/coderd/workspacequota.go index 67d07d91939ca..0416324508cb1 100644 --- a/enterprise/coderd/workspacequota.go +++ b/enterprise/coderd/workspacequota.go @@ -99,6 +99,14 @@ func (c *committer) CommitQuota( }, nil } +// @Summary Get workspace quota by user +// @ID get-workspace-quota-by-user +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param user path string true "User ID, name, or me" +// @Success 200 {object} codersdk.WorkspaceQuota +// @Router /workspace-quota/{user} [get] func (api *API) workspaceQuota(rw http.ResponseWriter, r *http.Request) { user := httpmw.UserParam(r) From a7f6add11d7426fc7d23956570f6539c58e2b5c9 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 15:28:15 +0100 Subject: [PATCH 38/61] Fix --- coderd/apidoc/docs.go | 4 +- coderd/apidoc/swagger.json | 4 +- codersdk/replicas.go | 4 +- docs/api/agents.md | 3 - docs/api/builds.md | 280 ++++++++-------- docs/api/enterprise.md | 34 +- docs/api/schemas.md | 4 +- docs/api/templates.md | 306 +++++++++--------- docs/api/users.md | 24 +- .../apidocgen/markdown-template/responses.def | 37 ++- 10 files changed, 366 insertions(+), 334 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index dcdab5ce2b602..1e6bc305bedbd 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -6207,7 +6207,7 @@ const docTemplate = `{ "type": "object", "properties": { "created_at": { - "description": "CreatedAt is when the replica was first seen.", + "description": "CreatedAt is the timestamp when the replica was first seen.", "type": "string", "format": "date-time" }, @@ -6216,7 +6216,7 @@ const docTemplate = `{ "type": "integer" }, "error": { - "description": "Error is the error.", + "description": "Error is the replica error.", "type": "string" }, "hostname": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 7bdeba0b2471a..917c3ad96902b 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -5580,7 +5580,7 @@ "type": "object", "properties": { "created_at": { - "description": "CreatedAt is when the replica was first seen.", + "description": "CreatedAt is the timestamp when the replica was first seen.", "type": "string", "format": "date-time" }, @@ -5589,7 +5589,7 @@ "type": "integer" }, "error": { - "description": "Error is the error.", + "description": "Error is the replica error.", "type": "string" }, "hostname": { diff --git a/codersdk/replicas.go b/codersdk/replicas.go index ff44064db24ff..756ebcfcb756d 100644 --- a/codersdk/replicas.go +++ b/codersdk/replicas.go @@ -15,13 +15,13 @@ type Replica struct { ID uuid.UUID `json:"id" format:"uuid"` // Hostname is the hostname of the replica. Hostname string `json:"hostname"` - // CreatedAt is when the replica was first seen. + // CreatedAt is the timestamp when the replica was first seen. CreatedAt time.Time `json:"created_at" format:"date-time"` // RelayAddress is the accessible address to relay DERP connections. RelayAddress string `json:"relay_address"` // RegionID is the region of the replica. RegionID int32 `json:"region_id"` - // Error is the error. + // Error is the replica error. Error string `json:"error"` // DatabaseLatency is the latency in microseconds to the database. DatabaseLatency int32 `json:"database_latency"` diff --git a/docs/api/agents.md b/docs/api/agents.md index 4a02ff56dc637..3c3e9d60af4cd 100644 --- a/docs/api/agents.md +++ b/docs/api/agents.md @@ -192,9 +192,6 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/coordinate \ `GET /workspaceagents/me/coordinate` -It accepts a WebSocket connection to an agent that listens to -incoming connections and publishes node updates. - ### Responses | Status | Meaning | Description | Schema | diff --git a/docs/api/builds.md b/docs/api/builds.md index 5a58c4b33291f..245c183dd5109 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -513,61 +513,61 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------- | ---------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» agents` | array | false | | | -| `»» apps` | array | false | | | -| `»»» command` | string | false | | | -| `»»» display_name` | string | false | | DisplayName is a friendly name for the app. | -| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on
the client or not. | -| `»»» health` | string | false | | | -| `»»» healthcheck` | `codersdk.Healthcheck` | false | | | -| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | -| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies
an icon to be displayed in the dashboard. | -| `»»» id` | string(uuid) | false | | | -| `»»» sharing_level` | string | false | | | -| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the
`coder server` or via a hostname-based dev URL. If this is set to true
and there is no app wildcard configured on the server, the app will not
be accessible in the UI. | -| `»»» url` | string | false | | URL is the address being proxied to inside the workspace.
If external is specified, this will be opened on the client. | -| `»» architecture` | string | false | | | -| `»» connection_timeout_seconds` | integer | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» directory` | string | false | | | -| `»» disconnected_at` | string(date-time) | false | | | -| `»» environment_variables` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» first_connected_at` | string(date-time) | false | | | -| `»» id` | string(uuid) | false | | | -| `»» instance_id` | string | false | | | -| `»» last_connected_at` | string(date-time) | false | | | -| `»» latency` | object | false | | DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»» [any property]` | `codersdk.DERPRegion` | false | | | -| `»»»» latency_ms` | number | false | | | -| `»»»» preferred` | boolean | false | | | -| `»» name` | string | false | | | -| `»» operating_system` | string | false | | | -| `»» resource_id` | string(uuid) | false | | | -| `»» startup_script` | string | false | | | -| `»» status` | string | false | | | -| `»» troubleshooting_url` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» version` | string | false | | | -| `» created_at` | string(date-time) | false | | | -| `» daily_cost` | integer | false | | | -| `» hide` | boolean | false | | | -| `» icon` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» job_id` | string(uuid) | false | | | -| `» metadata` | array | false | | | -| `»» key` | string | false | | | -| `»» sensitive` | boolean | false | | | -| `»» value` | string | false | | | -| `» name` | string | false | | | -| `» type` | string | false | | | -| `» workspace_transition` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | ---------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | »»display name is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | +| `»»» health` | string | false | | | +| `»»» healthcheck` | `codersdk.Healthcheck` | false | | | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | »»»url specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `»»» id` | string(uuid) | false | | | +| `»»» sharing_level` | string | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | +| `»»» url` | string | false | | »»url is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string(date-time) | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» first_connected_at` | string(date-time) | false | | | +| `»» id` | string(uuid) | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string(date-time) | false | | | +| `»» latency` | object | false | | »latency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | `codersdk.DERPRegion` | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» resource_id` | string(uuid) | false | | | +| `»» startup_script` | string | false | | | +| `»» status` | string | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» version` | string | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | string | false | | | #### Enumerated Values @@ -878,91 +878,91 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{id}/builds \ Status Code **200** -| Name | Type | Required | Restrictions | Description | -| -------------------------------- | ------------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» build_number` | integer | false | | | -| `» created_at` | string(date-time) | false | | | -| `» daily_cost` | integer | false | | | -| `» deadline` | string(date-time) | false | | | -| `» id` | string(uuid) | false | | | -| `» initiator_id` | string(uuid) | false | | | -| `» initiator_name` | string | false | | | -| `» job` | `codersdk.ProvisionerJob` | false | | | -| `»» canceled_at` | string(date-time) | false | | | -| `»» completed_at` | string(date-time) | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» error` | string | false | | | -| `»» file_id` | string(uuid) | false | | | -| `»» id` | string(uuid) | false | | | -| `»» started_at` | string(date-time) | false | | | -| `»» status` | string | false | | | -| `»» tags` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» worker_id` | string(uuid) | false | | | -| `» reason` | string | false | | | -| `» resources` | array | false | | | -| `»» agents` | array | false | | | -| `»»» apps` | array | false | | | -| `»»»» command` | string | false | | | -| `»»»» display_name` | string | false | | DisplayName is a friendly name for the app. | -| `»»»» external` | boolean | false | | External specifies whether the URL should be opened externally on
the client or not. | -| `»»»» health` | string | false | | | -| `»»»» healthcheck` | `codersdk.Healthcheck` | false | | | -| `»»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | -| `»»»» icon` | string | false | | Icon is a relative path or external URL that specifies
an icon to be displayed in the dashboard. | -| `»»»» id` | string(uuid) | false | | | -| `»»»» sharing_level` | string | false | | | -| `»»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the
`coder server` or via a hostname-based dev URL. If this is set to true
and there is no app wildcard configured on the server, the app will not
be accessible in the UI. | -| `»»»» url` | string | false | | URL is the address being proxied to inside the workspace.
If external is specified, this will be opened on the client. | -| `»»» architecture` | string | false | | | -| `»»» connection_timeout_seconds` | integer | false | | | -| `»»» created_at` | string(date-time) | false | | | -| `»»» directory` | string | false | | | -| `»»» disconnected_at` | string(date-time) | false | | | -| `»»» environment_variables` | object | false | | | -| `»»»» [any property]` | string | false | | | -| `»»» first_connected_at` | string(date-time) | false | | | -| `»»» id` | string(uuid) | false | | | -| `»»» instance_id` | string | false | | | -| `»»» last_connected_at` | string(date-time) | false | | | -| `»»» latency` | object | false | | DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»»» [any property]` | `codersdk.DERPRegion` | false | | | -| `»»»»» latency_ms` | number | false | | | -| `»»»»» preferred` | boolean | false | | | -| `»»» name` | string | false | | | -| `»»» operating_system` | string | false | | | -| `»»» resource_id` | string(uuid) | false | | | -| `»»» startup_script` | string | false | | | -| `»»» status` | string | false | | | -| `»»» troubleshooting_url` | string | false | | | -| `»»» updated_at` | string(date-time) | false | | | -| `»»» version` | string | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» daily_cost` | integer | false | | | -| `»» hide` | boolean | false | | | -| `»» icon` | string | false | | | -| `»» id` | string(uuid) | false | | | -| `»» job_id` | string(uuid) | false | | | -| `»» metadata` | array | false | | | -| `»»» key` | string | false | | | -| `»»» sensitive` | boolean | false | | | -| `»»» value` | string | false | | | -| `»» name` | string | false | | | -| `»» type` | string | false | | | -| `»» workspace_transition` | string | false | | | -| `» status` | string | false | | | -| `» template_version_id` | string(uuid) | false | | | -| `» template_version_name` | string | false | | | -| `» transition` | string | false | | | -| `» updated_at` | string(date-time) | false | | | -| `» workspace_id` | string(uuid) | false | | | -| `» workspace_name` | string | false | | | -| `» workspace_owner_id` | string(uuid) | false | | | -| `» workspace_owner_name` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| -------------------------------- | ------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» build_number` | integer | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» deadline` | string(date-time) | false | | | +| `» id` | string(uuid) | false | | | +| `» initiator_id` | string(uuid) | false | | | +| `» initiator_name` | string | false | | | +| `» job` | `codersdk.ProvisionerJob` | false | | | +| `»» canceled_at` | string(date-time) | false | | | +| `»» completed_at` | string(date-time) | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» error` | string | false | | | +| `»» file_id` | string(uuid) | false | | | +| `»» id` | string(uuid) | false | | | +| `»» started_at` | string(date-time) | false | | | +| `»» status` | string | false | | | +| `»» tags` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» worker_id` | string(uuid) | false | | | +| `» reason` | string | false | | | +| `» resources` | array | false | | | +| `»» agents` | array | false | | | +| `»»» apps` | array | false | | | +| `»»»» command` | string | false | | | +| `»»»» display_name` | string | false | | »»»display name is a friendly name for the app. | +| `»»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | +| `»»»» health` | string | false | | | +| `»»»» healthcheck` | `codersdk.Healthcheck` | false | | | +| `»»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»»» url` | string | false | | »»»»url specifies the endpoint to check for the app health. | +| `»»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `»»»» id` | string(uuid) | false | | | +| `»»»» sharing_level` | string | false | | | +| `»»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | +| `»»»» url` | string | false | | »»»url is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +| `»»» architecture` | string | false | | | +| `»»» connection_timeout_seconds` | integer | false | | | +| `»»» created_at` | string(date-time) | false | | | +| `»»» directory` | string | false | | | +| `»»» disconnected_at` | string(date-time) | false | | | +| `»»» environment_variables` | object | false | | | +| `»»»» [any property]` | string | false | | | +| `»»» first_connected_at` | string(date-time) | false | | | +| `»»» id` | string(uuid) | false | | | +| `»»» instance_id` | string | false | | | +| `»»» last_connected_at` | string(date-time) | false | | | +| `»»» latency` | object | false | | »»latency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»»» [any property]` | `codersdk.DERPRegion` | false | | | +| `»»»»» latency_ms` | number | false | | | +| `»»»»» preferred` | boolean | false | | | +| `»»» name` | string | false | | | +| `»»» operating_system` | string | false | | | +| `»»» resource_id` | string(uuid) | false | | | +| `»»» startup_script` | string | false | | | +| `»»» status` | string | false | | | +| `»»» troubleshooting_url` | string | false | | | +| `»»» updated_at` | string(date-time) | false | | | +| `»»» version` | string | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» daily_cost` | integer | false | | | +| `»» hide` | boolean | false | | | +| `»» icon` | string | false | | | +| `»» id` | string(uuid) | false | | | +| `»» job_id` | string(uuid) | false | | | +| `»» metadata` | array | false | | | +| `»»» key` | string | false | | | +| `»»» sensitive` | boolean | false | | | +| `»»» value` | string | false | | | +| `»» name` | string | false | | | +| `»» type` | string | false | | | +| `»» workspace_transition` | string | false | | | +| `» status` | string | false | | | +| `» template_version_id` | string(uuid) | false | | | +| `» template_version_name` | string | false | | | +| `» transition` | string | false | | | +| `» updated_at` | string(date-time) | false | | | +| `» workspace_id` | string(uuid) | false | | | +| `» workspace_name` | string | false | | | +| `» workspace_owner_id` | string(uuid) | false | | | +| `» workspace_owner_name` | string | false | | | #### Enumerated Values diff --git a/docs/api/enterprise.md b/docs/api/enterprise.md index d3b645d7cba76..418a3e8aa8758 100644 --- a/docs/api/enterprise.md +++ b/docs/api/enterprise.md @@ -359,13 +359,13 @@ curl -X GET http://coder-server:8080/api/v2/licenses \ Status Code **200** -| Name | Type | Required | Restrictions | Description | -| --------------- | ----------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» claims` | object | false | | Claims are the JWT claims asserted by the license. Here we use
a generic string map to ensure that all data from the server is
parsed verbatim, not just the fields this version of Coder
understands. | -| `» id` | integer | false | | | -| `» uploaded_at` | string(date-time) | false | | | -| `» uuid` | string(uuid) | false | | | +| Name | Type | Required | Restrictions | Description | +| --------------- | ----------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `[array item]` | array | false | | | +| `» claims` | object | false | | Claims are the JWT claims asserted by the license. Here we use a generic string map to ensure that all data from the server is parsed verbatim, not just the fields this version of Coder understands. | +| `» id` | integer | false | | | +| `» uploaded_at` | string(date-time) | false | | | +| `» uuid` | string(uuid) | false | | | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -596,16 +596,16 @@ curl -X GET http://coder-server:8080/api/v2/replicas \ Status Code **200** -| Name | Type | Required | Restrictions | Description | -| -------------------- | ----------------- | -------- | ------------ | ----------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» created_at` | string(date-time) | false | | CreatedAt is when the replica was first seen. | -| `» database_latency` | integer | false | | DatabaseLatency is the latency in microseconds to the database. | -| `» error` | string | false | | Error is the error. | -| `» hostname` | string | false | | Hostname is the hostname of the replica. | -| `» id` | string(uuid) | false | | ID is the unique identifier for the replica. | -| `» region_id` | integer | false | | RegionID is the region of the replica. | -| `» relay_address` | string | false | | RelayAddress is the accessible address to relay DERP connections. | +| Name | Type | Required | Restrictions | Description | +| -------------------- | ----------------- | -------- | ------------ | ------------------------------------------------------------------ | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | false | | Created at is the timestamp when the replica was first seen. | +| `» database_latency` | integer | false | | Database latency is the latency in microseconds to the database. | +| `» error` | string | false | | Error is the replica error. | +| `» hostname` | string | false | | Hostname is the hostname of the replica. | +| `» id` | string(uuid) | false | | ID is the unique identifier for the replica. | +| `» region_id` | integer | false | | Region ID is the region of the replica. | +| `» relay_address` | string | false | | Relay address is the accessible address to relay DERP connections. | To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 109cc2f8aa49e..e0655dd194fac 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -3209,9 +3209,9 @@ Parameter represents a set value for the scope. | Name | Type | Required | Restrictions | Description | | ------------------ | ------- | -------- | ------------ | ------------------------------------------------------------------ | -| `created_at` | string | false | | Created at is when the replica was first seen. | +| `created_at` | string | false | | Created at is the timestamp when the replica was first seen. | | `database_latency` | integer | false | | Database latency is the latency in microseconds to the database. | -| `error` | string | false | | Error is the error. | +| `error` | string | false | | Error is the replica error. | | `hostname` | string | false | | Hostname is the hostname of the replica. | | `id` | string | false | | ID is the unique identifier for the replica. | | `region_id` | integer | false | | Region ID is the region of the replica. | diff --git a/docs/api/templates.md b/docs/api/templates.md index ca64657f93beb..b264757be8453 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -139,29 +139,29 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------------ | --------------------------------- | -------- | ------------ | ------------------------------------------ | -| `[array item]` | array | false | | | -| `» active_user_count` | integer | false | | ActiveUserCount is set to -1 when loading. | -| `» active_version_id` | string(uuid) | false | | | -| `» allow_user_cancel_workspace_jobs` | boolean | false | | | -| `» build_time_stats` | `codersdk.TemplateBuildTimeStats` | false | | | -| `»» [any property]` | `codersdk.TransitionStats` | false | | | -| `»»» p50` | integer | false | | | -| `»»» p95` | integer | false | | | -| `» created_at` | string(date-time) | false | | | -| `» created_by_id` | string(uuid) | false | | | -| `» created_by_name` | string | false | | | -| `» default_ttl_ms` | integer | false | | | -| `» description` | string | false | | | -| `» display_name` | string | false | | | -| `» icon` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» name` | string | false | | | -| `» organization_id` | string(uuid) | false | | | -| `» provisioner` | string | false | | | -| `» updated_at` | string(date-time) | false | | | -| `» workspace_owner_count` | integer | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------------ | --------------------------------- | -------- | ------------ | -------------------------------------------- | +| `[array item]` | array | false | | | +| `» active_user_count` | integer | false | | Active user count is set to -1 when loading. | +| `» active_version_id` | string(uuid) | false | | | +| `» allow_user_cancel_workspace_jobs` | boolean | false | | | +| `» build_time_stats` | `codersdk.TemplateBuildTimeStats` | false | | | +| `»» [any property]` | `codersdk.TransitionStats` | false | | | +| `»»» p50` | integer | false | | | +| `»»» p95` | integer | false | | | +| `» created_at` | string(date-time) | false | | | +| `» created_by_id` | string(uuid) | false | | | +| `» created_by_name` | string | false | | | +| `» default_ttl_ms` | integer | false | | | +| `» description` | string | false | | | +| `» display_name` | string | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» name` | string | false | | | +| `» organization_id` | string(uuid) | false | | | +| `» provisioner` | string | false | | | +| `» updated_at` | string(date-time) | false | | | +| `» workspace_owner_count` | integer | false | | | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -1605,61 +1605,61 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------- | ---------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» agents` | array | false | | | -| `»» apps` | array | false | | | -| `»»» command` | string | false | | | -| `»»» display_name` | string | false | | DisplayName is a friendly name for the app. | -| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on
the client or not. | -| `»»» health` | string | false | | | -| `»»» healthcheck` | `codersdk.Healthcheck` | false | | | -| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | -| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies
an icon to be displayed in the dashboard. | -| `»»» id` | string(uuid) | false | | | -| `»»» sharing_level` | string | false | | | -| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the
`coder server` or via a hostname-based dev URL. If this is set to true
and there is no app wildcard configured on the server, the app will not
be accessible in the UI. | -| `»»» url` | string | false | | URL is the address being proxied to inside the workspace.
If external is specified, this will be opened on the client. | -| `»» architecture` | string | false | | | -| `»» connection_timeout_seconds` | integer | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» directory` | string | false | | | -| `»» disconnected_at` | string(date-time) | false | | | -| `»» environment_variables` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» first_connected_at` | string(date-time) | false | | | -| `»» id` | string(uuid) | false | | | -| `»» instance_id` | string | false | | | -| `»» last_connected_at` | string(date-time) | false | | | -| `»» latency` | object | false | | DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»» [any property]` | `codersdk.DERPRegion` | false | | | -| `»»»» latency_ms` | number | false | | | -| `»»»» preferred` | boolean | false | | | -| `»» name` | string | false | | | -| `»» operating_system` | string | false | | | -| `»» resource_id` | string(uuid) | false | | | -| `»» startup_script` | string | false | | | -| `»» status` | string | false | | | -| `»» troubleshooting_url` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» version` | string | false | | | -| `» created_at` | string(date-time) | false | | | -| `» daily_cost` | integer | false | | | -| `» hide` | boolean | false | | | -| `» icon` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» job_id` | string(uuid) | false | | | -| `» metadata` | array | false | | | -| `»» key` | string | false | | | -| `»» sensitive` | boolean | false | | | -| `»» value` | string | false | | | -| `» name` | string | false | | | -| `» type` | string | false | | | -| `» workspace_transition` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | ---------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | »»display name is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | +| `»»» health` | string | false | | | +| `»»» healthcheck` | `codersdk.Healthcheck` | false | | | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | »»»url specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `»»» id` | string(uuid) | false | | | +| `»»» sharing_level` | string | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | +| `»»» url` | string | false | | »»url is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string(date-time) | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» first_connected_at` | string(date-time) | false | | | +| `»» id` | string(uuid) | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string(date-time) | false | | | +| `»» latency` | object | false | | »latency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | `codersdk.DERPRegion` | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» resource_id` | string(uuid) | false | | | +| `»» startup_script` | string | false | | | +| `»» status` | string | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» version` | string | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | string | false | | | #### Enumerated Values @@ -1927,61 +1927,61 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------- | ---------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» agents` | array | false | | | -| `»» apps` | array | false | | | -| `»»» command` | string | false | | | -| `»»» display_name` | string | false | | DisplayName is a friendly name for the app. | -| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on
the client or not. | -| `»»» health` | string | false | | | -| `»»» healthcheck` | `codersdk.Healthcheck` | false | | | -| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | -| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies
an icon to be displayed in the dashboard. | -| `»»» id` | string(uuid) | false | | | -| `»»» sharing_level` | string | false | | | -| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the
`coder server` or via a hostname-based dev URL. If this is set to true
and there is no app wildcard configured on the server, the app will not
be accessible in the UI. | -| `»»» url` | string | false | | URL is the address being proxied to inside the workspace.
If external is specified, this will be opened on the client. | -| `»» architecture` | string | false | | | -| `»» connection_timeout_seconds` | integer | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» directory` | string | false | | | -| `»» disconnected_at` | string(date-time) | false | | | -| `»» environment_variables` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» first_connected_at` | string(date-time) | false | | | -| `»» id` | string(uuid) | false | | | -| `»» instance_id` | string | false | | | -| `»» last_connected_at` | string(date-time) | false | | | -| `»» latency` | object | false | | DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»» [any property]` | `codersdk.DERPRegion` | false | | | -| `»»»» latency_ms` | number | false | | | -| `»»»» preferred` | boolean | false | | | -| `»» name` | string | false | | | -| `»» operating_system` | string | false | | | -| `»» resource_id` | string(uuid) | false | | | -| `»» startup_script` | string | false | | | -| `»» status` | string | false | | | -| `»» troubleshooting_url` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» version` | string | false | | | -| `» created_at` | string(date-time) | false | | | -| `» daily_cost` | integer | false | | | -| `» hide` | boolean | false | | | -| `» icon` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» job_id` | string(uuid) | false | | | -| `» metadata` | array | false | | | -| `»» key` | string | false | | | -| `»» sensitive` | boolean | false | | | -| `»» value` | string | false | | | -| `» name` | string | false | | | -| `» type` | string | false | | | -| `» workspace_transition` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | ---------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | »»display name is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | +| `»»» health` | string | false | | | +| `»»» healthcheck` | `codersdk.Healthcheck` | false | | | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | »»»url specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `»»» id` | string(uuid) | false | | | +| `»»» sharing_level` | string | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | +| `»»» url` | string | false | | »»url is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string(date-time) | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» first_connected_at` | string(date-time) | false | | | +| `»» id` | string(uuid) | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string(date-time) | false | | | +| `»» latency` | object | false | | »latency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | `codersdk.DERPRegion` | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» resource_id` | string(uuid) | false | | | +| `»» startup_script` | string | false | | | +| `»» status` | string | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» version` | string | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | string | false | | | #### Enumerated Values @@ -2057,26 +2057,26 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/s Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------ | ----------------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» allow_override_destination` | boolean | false | | | -| `» allow_override_source` | boolean | false | | | -| `» created_at` | string(date-time) | false | | | -| `» default_destination_scheme` | string | false | | | -| `» default_refresh` | string | false | | | -| `» default_source_scheme` | string | false | | | -| `» default_source_value` | string | false | | | -| `» description` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» job_id` | string(uuid) | false | | | -| `» name` | string | false | | | -| `» redisplay_value` | boolean | false | | | -| `» validation_condition` | string | false | | | -| `» validation_contains` | array | false | | This is a special array of items provided if the validation condition
explicitly states the value must be one of a set. | -| `» validation_error` | string | false | | | -| `» validation_type_system` | string | false | | | -| `» validation_value_type` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------ | ----------------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» allow_override_destination` | boolean | false | | | +| `» allow_override_source` | boolean | false | | | +| `» created_at` | string(date-time) | false | | | +| `» default_destination_scheme` | string | false | | | +| `» default_refresh` | string | false | | | +| `» default_source_scheme` | string | false | | | +| `» default_source_value` | string | false | | | +| `» description` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | +| `» name` | string | false | | | +| `» redisplay_value` | boolean | false | | | +| `» validation_condition` | string | false | | | +| `» validation_contains` | array | false | | This is a special array of items provided if the validation condition explicitly states the value must be one of a set. | +| `» validation_error` | string | false | | | +| `» validation_type_system` | string | false | | | +| `» validation_value_type` | string | false | | | #### Enumerated Values diff --git a/docs/api/users.md b/docs/api/users.md index 9665d99be9ed7..c2ff4c5aade1c 100644 --- a/docs/api/users.md +++ b/docs/api/users.md @@ -587,18 +587,18 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/keys/tokens \ Status Code **200** -| Name | Type | Required | Restrictions | Description | -| -------------------- | ----------------- | -------- | ------------ | ----------------------------------------- | -| `[array item]` | array | false | | | -| `» created_at` | string(date-time) | true | | | -| `» expires_at` | string(date-time) | true | | | -| `» id` | string | true | | | -| `» last_used` | string(date-time) | true | | | -| `» lifetime_seconds` | integer | true | | | -| `» login_type` | string | true | | | -| `» scope` | string | true | | | -| `» updated_at` | string(date-time) | true | | | -| `» user_id` | string(uuid) | true | | NOTE: do not ever return the HashedSecret | +| Name | Type | Required | Restrictions | Description | +| -------------------- | ----------------- | -------- | ------------ | ------------------------------------------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | true | | | +| `» expires_at` | string(date-time) | true | | | +| `» id` | string | true | | | +| `» last_used` | string(date-time) | true | | | +| `» lifetime_seconds` | integer | true | | | +| `» login_type` | string | true | | | +| `» scope` | string | true | | | +| `» updated_at` | string(date-time) | true | | | +| `» user_id` | string(uuid) | true | | User ID do not ever return the HashedSecret | #### Enumerated Values diff --git a/scripts/apidocgen/markdown-template/responses.def b/scripts/apidocgen/markdown-template/responses.def index 8fe3e36c4f240..543044ff685b8 100644 --- a/scripts/apidocgen/markdown-template/responses.def +++ b/scripts/apidocgen/markdown-template/responses.def @@ -44,6 +44,41 @@ return p.safeType; } + + function correctLetterCase(description) { + const dictionary = ["id", "url"]; + + const words = description.split(' '); + var corrected = []; + words.forEach(word => { + if (dictionary.includes(word.toLowerCase())) { + corrected.push(word.toUpperCase()); + return; + } + corrected.push(word); + }); + return corrected.join(" "); + } + + function renderDescription(p) { + if (!p.description) { + return ""; + } + + let description = p.description.replaceAll("

", "\n").replaceAll("
", " "); + const words = description.split(' '); + if (words.length == 0) { + return ""; + } + + const countUppercase = words[0].length - words[0].replace(/[A-Z]/g, '').length; + if (countUppercase > 1) { + let displayName = p.displayName.replaceAll("» **additionalProperties**", "It").replace("» ", ""); + displayName = displayName.charAt(0).toUpperCase() + displayName.replaceAll("_", " ").toLowerCase().slice(1); + description = displayName + " " + words.slice(1).join(' '); + } + return correctLetterCase(description); + } }} {{ data.responses = data.utils.getResponses(data); }} {{ data.responseSchemas = false; }} @@ -97,7 +132,7 @@ Status Code **{{=response.status}}** {{?}} |Name|Type|Required|Restrictions|Description| |---|---|---|---|---| -{{~block.rows :p}}|`{{=renderDisplayName(p)}}`|{{=renderResponsePropertyType(p)}}|{{=p.required}}|{{=p.restrictions||''}}|{{=p.description||''}}| +{{~block.rows :p}}|`{{=renderDisplayName(p)}}`|{{=renderResponsePropertyType(p)}}|{{=p.required}}|{{=p.restrictions||''}}|{{=renderDescription(p)}}| {{~}} {{~}} {{?}} From 3ecfad7c1442039b25b62f45cc789d609bc5fa68 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 15:30:53 +0100 Subject: [PATCH 39/61] Fix --- scripts/apidocgen/markdown-template/responses.def | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/apidocgen/markdown-template/responses.def b/scripts/apidocgen/markdown-template/responses.def index 543044ff685b8..8096ae7f8382d 100644 --- a/scripts/apidocgen/markdown-template/responses.def +++ b/scripts/apidocgen/markdown-template/responses.def @@ -65,7 +65,7 @@ return ""; } - let description = p.description.replaceAll("

", "\n").replaceAll("
", " "); + let description = p.description.replace(/

/g, "\n").replace(/
/g, " "); const words = description.split(' '); if (words.length == 0) { return ""; @@ -73,8 +73,8 @@ const countUppercase = words[0].length - words[0].replace(/[A-Z]/g, '').length; if (countUppercase > 1) { - let displayName = p.displayName.replaceAll("» **additionalProperties**", "It").replace("» ", ""); - displayName = displayName.charAt(0).toUpperCase() + displayName.replaceAll("_", " ").toLowerCase().slice(1); + let displayName = p.displayName.replace(/» \*\*additionalProperties\*\*/g, "It").replace("» ", ""); + displayName = displayName.charAt(0).toUpperCase() + displayName.replace(/_/g, " ").toLowerCase().slice(1); description = displayName + " " + words.slice(1).join(' '); } return correctLetterCase(description); From 05b6a984b5fc8c60777606e879adef8762c91757 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 9 Jan 2023 15:54:22 +0100 Subject: [PATCH 40/61] Clean templates --- docs/api/agents.md | 3 ++ scripts/apidocgen/markdown-template/debug.def | 1 - scripts/apidocgen/markdown-template/main.dot | 6 ++- .../apidocgen/markdown-template/responses.def | 37 +------------------ 4 files changed, 9 insertions(+), 38 deletions(-) delete mode 100644 scripts/apidocgen/markdown-template/debug.def diff --git a/docs/api/agents.md b/docs/api/agents.md index 3c3e9d60af4cd..4a02ff56dc637 100644 --- a/docs/api/agents.md +++ b/docs/api/agents.md @@ -192,6 +192,9 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/coordinate \ `GET /workspaceagents/me/coordinate` +It accepts a WebSocket connection to an agent that listens to +incoming connections and publishes node updates. + ### Responses | Status | Meaning | Description | Schema | diff --git a/scripts/apidocgen/markdown-template/debug.def b/scripts/apidocgen/markdown-template/debug.def deleted file mode 100644 index 04fcf8f761b80..0000000000000 --- a/scripts/apidocgen/markdown-template/debug.def +++ /dev/null @@ -1 +0,0 @@ -{{= data.utils.inspect(data) }} diff --git a/scripts/apidocgen/markdown-template/main.dot b/scripts/apidocgen/markdown-template/main.dot index 6c324570d353e..3f437eba60cb4 100644 --- a/scripts/apidocgen/markdown-template/main.dot +++ b/scripts/apidocgen/markdown-template/main.dot @@ -53,12 +53,16 @@ const countUppercase = words[0].length - words[0].replace(/[A-Z]/g, '').length; if (countUppercase > 1) { - let displayName = p.displayName.replace(/» \*\*additionalProperties\*\*/g, "It"); + let displayName = p.displayName.replace(/» \*\*additionalProperties\*\*/g, "It").replace("» ", ""); displayName = displayName.charAt(0).toUpperCase() + displayName.replace(/_/g, " ").toLowerCase().slice(1); description = displayName + " " + words.slice(1).join(' '); } return correctLetterCase(description); } + + /* Export functions that are used by other template partials. */ + data.functions = {}; + data.functions.renderDescription = renderDescription; }} {{? data.api.components && data.api.components.securitySchemes }}{{#def.security}}{{?}} diff --git a/scripts/apidocgen/markdown-template/responses.def b/scripts/apidocgen/markdown-template/responses.def index 8096ae7f8382d..09ed0af959ab5 100644 --- a/scripts/apidocgen/markdown-template/responses.def +++ b/scripts/apidocgen/markdown-template/responses.def @@ -44,41 +44,6 @@ return p.safeType; } - - function correctLetterCase(description) { - const dictionary = ["id", "url"]; - - const words = description.split(' '); - var corrected = []; - words.forEach(word => { - if (dictionary.includes(word.toLowerCase())) { - corrected.push(word.toUpperCase()); - return; - } - corrected.push(word); - }); - return corrected.join(" "); - } - - function renderDescription(p) { - if (!p.description) { - return ""; - } - - let description = p.description.replace(/

/g, "\n").replace(/
/g, " "); - const words = description.split(' '); - if (words.length == 0) { - return ""; - } - - const countUppercase = words[0].length - words[0].replace(/[A-Z]/g, '').length; - if (countUppercase > 1) { - let displayName = p.displayName.replace(/» \*\*additionalProperties\*\*/g, "It").replace("» ", ""); - displayName = displayName.charAt(0).toUpperCase() + displayName.replace(/_/g, " ").toLowerCase().slice(1); - description = displayName + " " + words.slice(1).join(' '); - } - return correctLetterCase(description); - } }} {{ data.responses = data.utils.getResponses(data); }} {{ data.responseSchemas = false; }} @@ -132,7 +97,7 @@ Status Code **{{=response.status}}** {{?}} |Name|Type|Required|Restrictions|Description| |---|---|---|---|---| -{{~block.rows :p}}|`{{=renderDisplayName(p)}}`|{{=renderResponsePropertyType(p)}}|{{=p.required}}|{{=p.restrictions||''}}|{{=renderDescription(p)}}| +{{~block.rows :p}}|`{{=renderDisplayName(p)}}`|{{=renderResponsePropertyType(p)}}|{{=p.required}}|{{=p.restrictions||''}}|{{=data.functions.renderDescription(p)}}| {{~}} {{~}} {{?}} From 1ebf4088481571b41257e1a2fdaf1e0f34865bfd Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 11 Jan 2023 14:21:05 +0100 Subject: [PATCH 41/61] Sort API pages --- docs/manifest.json | 52 +++++++++++++-------------- scripts/apidocgen/postprocess/main.go | 6 ++++ 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/docs/manifest.json b/docs/manifest.json index 250c9041076f2..87995420cf0f9 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -326,16 +326,8 @@ "icon_path": "./images/icons/api.svg", "children": [ { - "title": "Authentication", - "path": "./api/authentication.md" - }, - { - "title": "General", - "path": "./api/general.md" - }, - { - "title": "Enterprise", - "path": "./api/enterprise.md" + "title": "Agents", + "path": "./api/agents.md" }, { "title": "Applications", @@ -345,49 +337,57 @@ "title": "Audit", "path": "./api/audit.md" }, + { + "title": "Authentication", + "path": "./api/authentication.md" + }, { "title": "Authorization", "path": "./api/authorization.md" }, { - "title": "Files", - "path": "./api/files.md" + "title": "Builds", + "path": "./api/builds.md" }, { - "title": "Organizations", - "path": "./api/organizations.md" + "title": "Enterprise", + "path": "./api/enterprise.md" }, { - "title": "Templates", - "path": "./api/templates.md" + "title": "Files", + "path": "./api/files.md" + }, + { + "title": "General", + "path": "./api/general.md" }, { "title": "Members", "path": "./api/members.md" }, { - "title": "Workspaces", - "path": "./api/workspaces.md" + "title": "Organizations", + "path": "./api/organizations.md" }, { "title": "Parameters", "path": "./api/parameters.md" }, { - "title": "Users", - "path": "./api/users.md" + "title": "Schemas", + "path": "./api/schemas.md" }, { - "title": "Builds", - "path": "./api/builds.md" + "title": "Templates", + "path": "./api/templates.md" }, { - "title": "Agents", - "path": "./api/agents.md" + "title": "Users", + "path": "./api/users.md" }, { - "title": "Schemas", - "path": "./api/schemas.md" + "title": "Workspaces", + "path": "./api/workspaces.md" } ] } diff --git a/scripts/apidocgen/postprocess/main.go b/scripts/apidocgen/postprocess/main.go index 2b747d07d1d7a..b593094ac94c4 100644 --- a/scripts/apidocgen/postprocess/main.go +++ b/scripts/apidocgen/postprocess/main.go @@ -9,6 +9,7 @@ import ( "os" "path" "regexp" + "sort" "strings" "golang.org/x/xerrors" @@ -132,6 +133,11 @@ func writeDocs(sections [][]byte) error { }) } + // Sort API pages + sort.Slice(mdFiles, func(i, j int) bool { + return sort.StringsAreSorted([]string{mdFiles[i].title, mdFiles[j].title}) + }) + // Update manifest.json type route struct { Title string `json:"title,omitempty"` From c2977cf3c1fbea9903038cddacf26c8c28f3c41b Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 11 Jan 2023 14:33:47 +0100 Subject: [PATCH 42/61] Fix: HashedSecret --- coderd/apidoc/docs.go | 1 - coderd/apidoc/swagger.json | 1 - codersdk/apikey.go | 4 ++-- docs/api/schemas.md | 22 +++++++++++----------- docs/api/users.md | 24 ++++++++++++------------ 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 88d0c34ef2aa2..c103fa9ca0ec9 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -4607,7 +4607,6 @@ const docTemplate = `{ "format": "date-time" }, "user_id": { - "description": "NOTE: do not ever return the HashedSecret", "type": "string", "format": "uuid" } diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 00560a74117ff..ae8733e3a44fe 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -4068,7 +4068,6 @@ "format": "date-time" }, "user_id": { - "description": "NOTE: do not ever return the HashedSecret", "type": "string", "format": "uuid" } diff --git a/codersdk/apikey.go b/codersdk/apikey.go index fd896a0d29b4a..739fe7d255a3c 100644 --- a/codersdk/apikey.go +++ b/codersdk/apikey.go @@ -10,9 +10,9 @@ import ( "github.com/google/uuid" ) +// APIKey: do not ever return the HashedSecret type APIKey struct { - ID string `json:"id" validate:"required"` - // NOTE: do not ever return the HashedSecret + ID string `json:"id" validate:"required"` UserID uuid.UUID `json:"user_id" validate:"required" format:"uuid"` LastUsed time.Time `json:"last_used" validate:"required" format:"date-time"` ExpiresAt time.Time `json:"expires_at" validate:"required" format:"date-time"` diff --git a/docs/api/schemas.md b/docs/api/schemas.md index f7bdf58d04f12..ed546daf87ca8 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -81,17 +81,17 @@ ### Properties -| Name | Type | Required | Restrictions | Description | -| ------------------ | -------------------------------------------- | -------- | ------------ | ------------------------------------------- | -| `created_at` | string | true | | | -| `expires_at` | string | true | | | -| `id` | string | true | | | -| `last_used` | string | true | | | -| `lifetime_seconds` | integer | true | | | -| `login_type` | [codersdk.LoginType](#codersdklogintype) | true | | | -| `scope` | [codersdk.APIKeyScope](#codersdkapikeyscope) | true | | | -| `updated_at` | string | true | | | -| `user_id` | string | true | | User ID do not ever return the HashedSecret | +| Name | Type | Required | Restrictions | Description | +| ------------------ | -------------------------------------------- | -------- | ------------ | ----------- | +| `created_at` | string | true | | | +| `expires_at` | string | true | | | +| `id` | string | true | | | +| `last_used` | string | true | | | +| `lifetime_seconds` | integer | true | | | +| `login_type` | [codersdk.LoginType](#codersdklogintype) | true | | | +| `scope` | [codersdk.APIKeyScope](#codersdkapikeyscope) | true | | | +| `updated_at` | string | true | | | +| `user_id` | string | true | | | #### Enumerated Values diff --git a/docs/api/users.md b/docs/api/users.md index 81dcda04c2755..a01027eea638c 100644 --- a/docs/api/users.md +++ b/docs/api/users.md @@ -587,18 +587,18 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/keys/tokens \ Status Code **200** -| Name | Type | Required | Restrictions | Description | -| -------------------- | ------------------------------------------------------ | -------- | ------------ | ------------------------------------------- | -| `[array item]` | array | false | | | -| `» created_at` | string(date-time) | true | | | -| `» expires_at` | string(date-time) | true | | | -| `» id` | string | true | | | -| `» last_used` | string(date-time) | true | | | -| `» lifetime_seconds` | integer | true | | | -| `» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | true | | | -| `» scope` | [codersdk.APIKeyScope](schemas.md#codersdkapikeyscope) | true | | | -| `» updated_at` | string(date-time) | true | | | -| `» user_id` | string(uuid) | true | | User ID do not ever return the HashedSecret | +| Name | Type | Required | Restrictions | Description | +| -------------------- | ------------------------------------------------------ | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | true | | | +| `» expires_at` | string(date-time) | true | | | +| `» id` | string | true | | | +| `» last_used` | string(date-time) | true | | | +| `» lifetime_seconds` | integer | true | | | +| `» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | true | | | +| `» scope` | [codersdk.APIKeyScope](schemas.md#codersdkapikeyscope) | true | | | +| `» updated_at` | string(date-time) | true | | | +| `» user_id` | string(uuid) | true | | | #### Enumerated Values From cbacf7656add712441c8ebe3c96aa4125d0f1664 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 11 Jan 2023 15:39:05 +0100 Subject: [PATCH 43/61] WIP --- coderd/coderdtest/swagger_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 coderd/coderdtest/swagger_test.go diff --git a/coderd/coderdtest/swagger_test.go b/coderd/coderdtest/swagger_test.go new file mode 100644 index 0000000000000..84a3031dbe0a1 --- /dev/null +++ b/coderd/coderdtest/swagger_test.go @@ -0,0 +1,18 @@ +package coderdtest + +import ( + "net/http" + "testing" + + "github.com/go-chi/chi/v5" + "github.com/stretchr/testify/require" +) + +func TestAllEndpointsDocumented(t *testing.T) { + _, _, api := NewWithAPI(t, nil) + + err := chi.Walk(api.APIHandler, func(method, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { + return nil + }) + require.NoError(t, err) +} From b23edb40d03ecf2b224c547463f155a0b5c16528 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 11 Jan 2023 19:27:37 +0100 Subject: [PATCH 44/61] WIP --- coderd/coderdtest/swagger_test.go | 68 +++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/coderd/coderdtest/swagger_test.go b/coderd/coderdtest/swagger_test.go index 84a3031dbe0a1..2e69efd5f496f 100644 --- a/coderd/coderdtest/swagger_test.go +++ b/coderd/coderdtest/swagger_test.go @@ -1,18 +1,80 @@ package coderdtest import ( + "go/ast" + "go/parser" + "go/token" "net/http" + "strings" "testing" "github.com/go-chi/chi/v5" "github.com/stretchr/testify/require" + "golang.org/x/xerrors" ) +type swaggerComment struct { + method string + router string +} + func TestAllEndpointsDocumented(t *testing.T) { - _, _, api := NewWithAPI(t, nil) + t.Parallel() + + swaggerCommentss, err := parseSwaggerComments("..") // TODO enterprise + require.NoError(t, err, "can't parse swagger comments") - err := chi.Walk(api.APIHandler, func(method, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { + for _, c := range swaggerCommentss { + t.Log(c.method, c.router) + } + + _, _, api := NewWithAPI(t, nil) + err = chi.Walk(api.APIHandler, func(method, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { return nil }) - require.NoError(t, err) + require.Error(t, err) +} + +func parseSwaggerComments(dir string) ([]swaggerComment, error) { + fileSet := token.NewFileSet() + commentNodes, err := parser.ParseDir(fileSet, dir, nil, parser.ParseComments) + if err != nil { + return nil, xerrors.Errorf(`can't parse directory "%s": %w`, dir) + } + + var swaggerComments []swaggerComment + for _, commentNode := range commentNodes { + ast.Inspect(commentNode, func(n ast.Node) bool { + commentGroup, ok := n.(*ast.CommentGroup) + if !ok { + return true + } + + var isSwaggerComment bool + for _, line := range commentGroup.List { + text := strings.TrimSpace(line.Text) + if strings.HasPrefix(text, "//") && strings.Contains(text, "@Router") { + isSwaggerComment = true + break + } + } + + if isSwaggerComment { + swaggerComments = append(swaggerComments, parseSwaggerComment(commentGroup)) + } + return true + }) + } + return swaggerComments, nil +} + +func parseSwaggerComment(commentGroup *ast.CommentGroup) swaggerComment { + var c swaggerComment + for _, line := range commentGroup.List { + text := strings.TrimSpace(line.Text) + if strings.Contains(text, "@Router ") { + c.router = strings.SplitN(text, " ", 3)[2] + } + } + return c } From 2f7013731d15e1f39367216df455fe99dc71dd48 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 10:11:32 +0100 Subject: [PATCH 45/61] WIP --- coderd/apidoc/docs.go | 360 +++++++++++++++--------------- coderd/apidoc/swagger.json | 338 ++++++++++++++-------------- coderd/coderdtest/swagger_test.go | 33 ++- coderd/templates.go | 16 +- coderd/templateversions.go | 26 +-- coderd/workspacebuilds.go | 8 +- coderd/workspaces.go | 2 +- docs/api/builds.md | 30 +-- docs/api/templates.md | 86 +++---- docs/api/workspaces.md | 4 +- 10 files changed, 457 insertions(+), 446 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index c103fa9ca0ec9..f868bedc069c1 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1576,7 +1576,7 @@ const docTemplate = `{ } } }, - "/templates/{id}": { + "/templates/{template}": { "get": { "security": [ { @@ -1596,7 +1596,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true } @@ -1629,7 +1629,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true } @@ -1662,7 +1662,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true } @@ -1677,7 +1677,7 @@ const docTemplate = `{ } } }, - "/templates/{id}/daus": { + "/templates/{template}/acl": { "get": { "security": [ { @@ -1688,16 +1688,16 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Templates" + "Enterprise" ], - "summary": "Get template DAUs by ID", - "operationId": "get-template-daus-by-id", + "summary": "Get template ACLs", + "operationId": "get-template-acls", "parameters": [ { "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true } @@ -1706,14 +1706,15 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.TemplateDAUsResponse" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateUser" + } } } } - } - }, - "/templates/{id}/versions": { - "get": { + }, + "patch": { "security": [ { "CoderSessionToken": [] @@ -1723,83 +1724,60 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Templates" + "Enterprise" ], - "summary": "List template versions by template ID", - "operationId": "list-template-versions-by-template-ID", + "summary": "Update template ACL", + "operationId": "update-template-acl", "parameters": [ { "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true }, { - "type": "string", - "format": "uuid", - "description": "After ID", - "name": "after_id", - "in": "query" - }, - { - "type": "integer", - "description": "Page limit", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Page offset", - "name": "offset", - "in": "query" + "description": "Update template request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateTemplateACL" + } } ], "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.TemplateVersion" - } + "$ref": "#/definitions/codersdk.Response" } } } - }, - "patch": { + } + }, + "/templates/{template}/daus": { + "get": { "security": [ { "CoderSessionToken": [] } ], - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], "tags": [ "Templates" ], - "summary": "Update active template version by template ID", - "operationId": "update-active-template-version-by-template-ID", + "summary": "Get template DAUs by ID", + "operationId": "get-template-daus-by-id", "parameters": [ - { - "description": "Modified template version", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateActiveTemplateVersion" - } - }, { "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true } @@ -1808,13 +1786,13 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.TemplateDAUsResponse" } } } } }, - "/templates/{id}/versions/{templateversionname}": { + "/templates/{template}/versions": { "get": { "security": [ { @@ -1827,23 +1805,35 @@ const docTemplate = `{ "tags": [ "Templates" ], - "summary": "Get template version by template ID and name", - "operationId": "get-template-version-by-template-id-and-name", + "summary": "List template versions by template ID", + "operationId": "list-template-versions-by-template-ID", "parameters": [ { "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true }, { "type": "string", - "description": "Template version name", - "name": "templateversionname", - "in": "path", - "required": true + "format": "uuid", + "description": "After ID", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" } ], "responses": { @@ -1857,24 +1847,34 @@ const docTemplate = `{ } } } - } - }, - "/templates/{template}/acl": { - "get": { + }, + "patch": { "security": [ { "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Enterprise" + "Templates" ], - "summary": "Get template ACLs", - "operationId": "get-template-acls", + "summary": "Update active template version by template ID", + "operationId": "update-active-template-version-by-template-ID", "parameters": [ + { + "description": "Modified template version", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateActiveTemplateVersion" + } + }, { "type": "string", "format": "uuid", @@ -1888,15 +1888,14 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.TemplateUser" - } + "$ref": "#/definitions/codersdk.Response" } } } - }, - "patch": { + } + }, + "/templates/{template}/versions/{templateversionname}": { + "get": { "security": [ { "CoderSessionToken": [] @@ -1906,10 +1905,10 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Enterprise" + "Templates" ], - "summary": "Update template ACL", - "operationId": "update-template-acl", + "summary": "Get template version by template ID and name", + "operationId": "get-template-version-by-template-id-and-name", "parameters": [ { "type": "string", @@ -1920,20 +1919,21 @@ const docTemplate = `{ "required": true }, { - "description": "Update template request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateTemplateACL" - } + "type": "string", + "description": "Template version name", + "name": "templateversionname", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateVersion" + } } } } @@ -2056,7 +2056,7 @@ const docTemplate = `{ } } }, - "/templateversions/{templateversion}/dry-run/{jobid}": { + "/templateversions/{templateversion}/dry-run/{jobID}": { "get": { "security": [ { @@ -2087,7 +2087,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Job ID", - "name": "jobid", + "name": "jobID", "in": "path", "required": true } @@ -2102,7 +2102,7 @@ const docTemplate = `{ } } }, - "/templateversions/{templateversion}/dry-run/{jobid}/cancel": { + "/templateversions/{templateversion}/dry-run/{jobID}/cancel": { "patch": { "security": [ { @@ -2137,7 +2137,7 @@ const docTemplate = `{ } } }, - "/templateversions/{templateversion}/dry-run/{jobid}/logs": { + "/templateversions/{templateversion}/dry-run/{jobID}/logs": { "get": { "security": [ { @@ -2165,7 +2165,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Job ID", - "name": "jobid", + "name": "job-ID", "in": "path", "required": true }, @@ -2201,7 +2201,7 @@ const docTemplate = `{ } } }, - "/templateversions/{templateversion}/dry-run/{jobid}/resources": { + "/templateversions/{templateversion}/dry-run/{jobID}/resources": { "get": { "security": [ { @@ -2229,7 +2229,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Job ID", - "name": "jobid", + "name": "jobID", "in": "path", "required": true } @@ -4110,7 +4110,7 @@ const docTemplate = `{ } } }, - "/workspaces/{id}": { + "/workspaces/{workspace}": { "get": { "security": [ { @@ -4149,132 +4149,109 @@ const docTemplate = `{ } } } - } - }, - "/workspaces/{id}/builds": { - "get": { + }, + "patch": { "security": [ { "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Builds" + "Workspaces" ], - "summary": "Get workspace builds by workspace ID", - "operationId": "get-workspace-builds-by-workspace-id", + "summary": "Update workspace metadata by ID", + "operationId": "update-workspace-metadata-by-id", "parameters": [ { "type": "string", "format": "uuid", "description": "Workspace ID", - "name": "id", + "name": "workspace", "in": "path", "required": true }, { - "type": "string", - "format": "uuid", - "description": "After ID", - "name": "after_id", - "in": "query" - }, - { - "type": "integer", - "description": "Page limit", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Page offset", - "name": "offset", - "in": "query" - }, - { - "type": "string", - "format": "date-time", - "description": "Since timestamp", - "name": "since", - "in": "query" + "description": "Metadata update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateWorkspaceRequest" + } } ], "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.WorkspaceBuild" - } - } + "204": { + "description": "No Content" } } - }, - "post": { + } + }, + "/workspaces/{workspace}/autostart": { + "put": { "security": [ { "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Builds" + "Workspaces" ], - "summary": "Create workspace build", - "operationId": "create-workspace-build", + "summary": "Update workspace autostart schedule by ID", + "operationId": "update-workspace-autostart-schedule-by-id", "parameters": [ { "type": "string", "format": "uuid", "description": "Workspace ID", - "name": "id", + "name": "workspace", "in": "path", "required": true }, { - "description": "Create workspace build request", + "description": "Schedule update request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.CreateWorkspaceBuildRequest" + "$ref": "#/definitions/codersdk.UpdateWorkspaceAutostartRequest" } } ], "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.WorkspaceBuild" - } + "204": { + "description": "No Content" } } } }, - "/workspaces/{workspace}": { - "patch": { + "/workspaces/{workspace}/builds": { + "get": { "security": [ { "CoderSessionToken": [] } ], - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], "tags": [ - "Workspaces" + "Builds" ], - "summary": "Update workspace metadata by ID", - "operationId": "update-workspace-metadata-by-id", + "summary": "Get workspace builds by workspace ID", + "operationId": "get-workspace-builds-by-workspace-id", "parameters": [ { "type": "string", @@ -4285,40 +4262,58 @@ const docTemplate = `{ "required": true }, { - "description": "Metadata update request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateWorkspaceRequest" - } + "type": "string", + "format": "uuid", + "description": "After ID", + "name": "after_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page limit", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Page offset", + "name": "offset", + "in": "query" + }, + { + "type": "string", + "format": "date-time", + "description": "Since timestamp", + "name": "since", + "in": "query" } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceBuild" + } + } } } - } - }, - "/workspaces/{workspace}/autostart": { - "put": { + }, + "post": { "security": [ { "CoderSessionToken": [] } ], - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], "tags": [ - "Workspaces" + "Builds" ], - "summary": "Update workspace autostart schedule by ID", - "operationId": "update-workspace-autostart-schedule-by-id", + "summary": "Create workspace build", + "operationId": "create-workspace-build", "parameters": [ { "type": "string", @@ -4329,18 +4324,21 @@ const docTemplate = `{ "required": true }, { - "description": "Schedule update request", + "description": "Create workspace build request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateWorkspaceAutostartRequest" + "$ref": "#/definitions/codersdk.CreateWorkspaceBuildRequest" } } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceBuild" + } } } } diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index ae8733e3a44fe..0c2ae1220c77b 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1376,7 +1376,7 @@ } } }, - "/templates/{id}": { + "/templates/{template}": { "get": { "security": [ { @@ -1392,7 +1392,7 @@ "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true } @@ -1421,7 +1421,7 @@ "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true } @@ -1450,7 +1450,7 @@ "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true } @@ -1465,7 +1465,79 @@ } } }, - "/templates/{id}/daus": { + "/templates/{template}/acl": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get template ACLs", + "operationId": "get-template-acls", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "template", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateUser" + } + } + } + } + }, + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Update template ACL", + "operationId": "update-template-acl", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "template", + "in": "path", + "required": true + }, + { + "description": "Update template request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateTemplateACL" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, + "/templates/{template}/daus": { "get": { "security": [ { @@ -1481,7 +1553,7 @@ "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true } @@ -1496,7 +1568,7 @@ } } }, - "/templates/{id}/versions": { + "/templates/{template}/versions": { "get": { "security": [ { @@ -1512,7 +1584,7 @@ "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true }, @@ -1573,7 +1645,7 @@ "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true } @@ -1588,7 +1660,7 @@ } } }, - "/templates/{id}/versions/{templateversionname}": { + "/templates/{template}/versions/{templateversionname}": { "get": { "security": [ { @@ -1604,7 +1676,7 @@ "type": "string", "format": "uuid", "description": "Template ID", - "name": "id", + "name": "template", "in": "path", "required": true }, @@ -1629,78 +1701,6 @@ } } }, - "/templates/{template}/acl": { - "get": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Get template ACLs", - "operationId": "get-template-acls", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Template ID", - "name": "template", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.TemplateUser" - } - } - } - } - }, - "patch": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Update template ACL", - "operationId": "update-template-acl", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Template ID", - "name": "template", - "in": "path", - "required": true - }, - { - "description": "Update template request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateTemplateACL" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } - } - } - } - }, "/templateversions/{templateversion}": { "get": { "security": [ @@ -1804,7 +1804,7 @@ } } }, - "/templateversions/{templateversion}/dry-run/{jobid}": { + "/templateversions/{templateversion}/dry-run/{jobID}": { "get": { "security": [ { @@ -1829,7 +1829,7 @@ "type": "string", "format": "uuid", "description": "Job ID", - "name": "jobid", + "name": "jobID", "in": "path", "required": true } @@ -1844,7 +1844,7 @@ } } }, - "/templateversions/{templateversion}/dry-run/{jobid}/cancel": { + "/templateversions/{templateversion}/dry-run/{jobID}/cancel": { "patch": { "security": [ { @@ -1875,7 +1875,7 @@ } } }, - "/templateversions/{templateversion}/dry-run/{jobid}/logs": { + "/templateversions/{templateversion}/dry-run/{jobID}/logs": { "get": { "security": [ { @@ -1899,7 +1899,7 @@ "type": "string", "format": "uuid", "description": "Job ID", - "name": "jobid", + "name": "job-ID", "in": "path", "required": true }, @@ -1935,7 +1935,7 @@ } } }, - "/templateversions/{templateversion}/dry-run/{jobid}/resources": { + "/templateversions/{templateversion}/dry-run/{jobID}/resources": { "get": { "security": [ { @@ -1959,7 +1959,7 @@ "type": "string", "format": "uuid", "description": "Job ID", - "name": "jobid", + "name": "jobID", "in": "path", "required": true } @@ -3619,7 +3619,7 @@ } } }, - "/workspaces/{id}": { + "/workspaces/{workspace}": { "get": { "security": [ { @@ -3654,9 +3654,83 @@ } } } + }, + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Workspaces"], + "summary": "Update workspace metadata by ID", + "operationId": "update-workspace-metadata-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace ID", + "name": "workspace", + "in": "path", + "required": true + }, + { + "description": "Metadata update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateWorkspaceRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/workspaces/{workspace}/autostart": { + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Workspaces"], + "summary": "Update workspace autostart schedule by ID", + "operationId": "update-workspace-autostart-schedule-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace ID", + "name": "workspace", + "in": "path", + "required": true + }, + { + "description": "Schedule update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateWorkspaceAutostartRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } } }, - "/workspaces/{id}/builds": { + "/workspaces/{workspace}/builds": { "get": { "security": [ { @@ -3672,7 +3746,7 @@ "type": "string", "format": "uuid", "description": "Workspace ID", - "name": "id", + "name": "workspace", "in": "path", "required": true }, @@ -3730,7 +3804,7 @@ "type": "string", "format": "uuid", "description": "Workspace ID", - "name": "id", + "name": "workspace", "in": "path", "required": true }, @@ -3754,82 +3828,6 @@ } } }, - "/workspaces/{workspace}": { - "patch": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["Workspaces"], - "summary": "Update workspace metadata by ID", - "operationId": "update-workspace-metadata-by-id", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Workspace ID", - "name": "workspace", - "in": "path", - "required": true - }, - { - "description": "Metadata update request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateWorkspaceRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/workspaces/{workspace}/autostart": { - "put": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["Workspaces"], - "summary": "Update workspace autostart schedule by ID", - "operationId": "update-workspace-autostart-schedule-by-id", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Workspace ID", - "name": "workspace", - "in": "path", - "required": true - }, - { - "description": "Schedule update request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateWorkspaceAutostartRequest" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, "/workspaces/{workspace}/extend": { "put": { "security": [ diff --git a/coderd/coderdtest/swagger_test.go b/coderd/coderdtest/swagger_test.go index 2e69efd5f496f..c0ed7694c5373 100644 --- a/coderd/coderdtest/swagger_test.go +++ b/coderd/coderdtest/swagger_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/go-chi/chi/v5" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/xerrors" ) @@ -21,25 +22,28 @@ type swaggerComment struct { func TestAllEndpointsDocumented(t *testing.T) { t.Parallel() - swaggerCommentss, err := parseSwaggerComments("..") // TODO enterprise + // TODO parse enterprise + swaggerComments, err := parseSwaggerComments("..") require.NoError(t, err, "can't parse swagger comments") - for _, c := range swaggerCommentss { - t.Log(c.method, c.router) - } - _, _, api := NewWithAPI(t, nil) - err = chi.Walk(api.APIHandler, func(method, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { + chi.Walk(api.APIHandler, func(method, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { + method = strings.ToLower(method) + if route != "/" && strings.HasSuffix(route, "/") { + route = route[:len(route)-1] + } + + c := findSwaggerCommentByMethodAndRoute(swaggerComments, method, route) + assert.NotNil(t, c, "Missing @Router annotation for: [%s] %s", method, route) return nil }) - require.Error(t, err) } func parseSwaggerComments(dir string) ([]swaggerComment, error) { fileSet := token.NewFileSet() commentNodes, err := parser.ParseDir(fileSet, dir, nil, parser.ParseComments) if err != nil { - return nil, xerrors.Errorf(`can't parse directory "%s": %w`, dir) + return nil, xerrors.Errorf(`parser.ParseDir failed "%s": %w`, dir, err) } var swaggerComments []swaggerComment @@ -73,8 +77,19 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) swaggerComment { for _, line := range commentGroup.List { text := strings.TrimSpace(line.Text) if strings.Contains(text, "@Router ") { - c.router = strings.SplitN(text, " ", 3)[2] + args := strings.SplitN(text, " ", 4) + c.router = args[2] + c.method = args[3][1 : len(args[3])-1] } } return c } + +func findSwaggerCommentByMethodAndRoute(comments []swaggerComment, method, route string) *swaggerComment { + for _, c := range comments { + if c.method == method && c.router == route { + return &c + } + } + return nil +} diff --git a/coderd/templates.go b/coderd/templates.go index c15a278810dfd..29e7e1b8c0b6d 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -28,9 +28,9 @@ import ( // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template ID" format(uuid) +// @Param template path string true "Template ID" format(uuid) // @Success 200 {object} codersdk.Template -// @Router /templates/{id} [get] +// @Router /templates/{template} [get] // Returns a single template. func (api *API) template(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -75,9 +75,9 @@ func (api *API) template(rw http.ResponseWriter, r *http.Request) { // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template ID" format(uuid) +// @Param template path string true "Template ID" format(uuid) // @Success 200 {object} codersdk.Response -// @Router /templates/{id} [delete] +// @Router /templates/{template} [delete] func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -465,9 +465,9 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template ID" format(uuid) +// @Param template path string true "Template ID" format(uuid) // @Success 200 {object} codersdk.Template -// @Router /templates/{id} [patch] +// @Router /templates/{template} [patch] func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -593,9 +593,9 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template ID" format(uuid) +// @Param template path string true "Template ID" format(uuid) // @Success 200 {object} codersdk.TemplateDAUsResponse -// @Router /templates/{id}/daus [get] +// @Router /templates/{template}/daus [get] func (api *API) templateDAUs(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() template := httpmw.TemplateParam(r) diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 15ac9a502711b..195a6c0d5916f 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -357,9 +357,9 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques // @Produce json // @Tags Templates // @Param templateversion path string true "Template version ID" format(uuid) -// @Param jobid path string true "Job ID" format(uuid) +// @Param jobID path string true "Job ID" format(uuid) // @Success 200 {object} codersdk.ProvisionerJob -// @Router /templateversions/{templateversion}/dry-run/{jobid} [get] +// @Router /templateversions/{templateversion}/dry-run/{jobID} [get] func (api *API) templateVersionDryRun(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() job, ok := api.fetchTemplateVersionDryRunJob(rw, r) @@ -376,9 +376,9 @@ func (api *API) templateVersionDryRun(rw http.ResponseWriter, r *http.Request) { // @Produce json // @Tags Templates // @Param templateversion path string true "Template version ID" format(uuid) -// @Param jobid path string true "Job ID" format(uuid) +// @Param jobID path string true "Job ID" format(uuid) // @Success 200 {array} codersdk.WorkspaceResource -// @Router /templateversions/{templateversion}/dry-run/{jobid}/resources [get] +// @Router /templateversions/{templateversion}/dry-run/{jobID}/resources [get] func (api *API) templateVersionDryRunResources(rw http.ResponseWriter, r *http.Request) { job, ok := api.fetchTemplateVersionDryRunJob(rw, r) if !ok { @@ -394,12 +394,12 @@ func (api *API) templateVersionDryRunResources(rw http.ResponseWriter, r *http.R // @Produce json // @Tags Templates // @Param templateversion path string true "Template version ID" format(uuid) -// @Param jobid path string true "Job ID" format(uuid) +// @Param job-ID path string true "Job ID" format(uuid) // @Param before query int false "Before Unix timestamp" // @Param after query int false "After Unix timestamp" // @Param follow query bool false "Follow log stream" // @Success 200 {array} codersdk.ProvisionerJobLog -// @Router /templateversions/{templateversion}/dry-run/{jobid}/logs [get] +// @Router /templateversions/{templateversion}/dry-run/{jobID}/logs [get] func (api *API) templateVersionDryRunLogs(rw http.ResponseWriter, r *http.Request) { job, ok := api.fetchTemplateVersionDryRunJob(rw, r) if !ok { @@ -416,7 +416,7 @@ func (api *API) templateVersionDryRunLogs(rw http.ResponseWriter, r *http.Reques // @Tags Templates // @Param templateversion path string true "Template version ID" format(uuid) // @Success 200 {object} codersdk.Response -// @Router /templateversions/{templateversion}/dry-run/{jobid}/cancel [patch] +// @Router /templateversions/{templateversion}/dry-run/{jobID}/cancel [patch] func (api *API) patchTemplateVersionDryRunCancel(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() templateVersion := httpmw.TemplateVersionParam(r) @@ -540,12 +540,12 @@ func (api *API) fetchTemplateVersionDryRunJob(rw http.ResponseWriter, r *http.Re // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template ID" format(uuid) +// @Param template path string true "Template ID" format(uuid) // @Param after_id query string false "After ID" format(uuid) // @Param limit query int false "Page limit" // @Param offset query int false "Page offset" // @Success 200 {array} codersdk.TemplateVersion -// @Router /templates/{id}/versions [get] +// @Router /templates/{template}/versions [get] func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() template := httpmw.TemplateParam(r) @@ -648,10 +648,10 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque // @Security CoderSessionToken // @Produce json // @Tags Templates -// @Param id path string true "Template ID" format(uuid) +// @Param template path string true "Template ID" format(uuid) // @Param templateversionname path string true "Template version name" // @Success 200 {array} codersdk.TemplateVersion -// @Router /templates/{id}/versions/{templateversionname} [get] +// @Router /templates/{template}/versions/{templateversionname} [get] func (api *API) templateVersionByName(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() template := httpmw.TemplateParam(r) @@ -834,9 +834,9 @@ func (api *API) previousTemplateVersionByOrganizationAndName(rw http.ResponseWri // @Produce json // @Tags Templates // @Param request body codersdk.UpdateActiveTemplateVersion true "Modified template version" -// @Param id path string true "Template ID" format(uuid) +// @Param template path string true "Template ID" format(uuid) // @Success 200 {object} codersdk.Response -// @Router /templates/{id}/versions [patch] +// @Router /templates/{template}/versions [patch] func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 3d3d42b7ea2a1..6b65fa3d2985f 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -77,13 +77,13 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) { // @Security CoderSessionToken // @Produce json // @Tags Builds -// @Param id path string true "Workspace ID" format(uuid) +// @Param workspace path string true "Workspace ID" format(uuid) // @Param after_id query string false "After ID" format(uuid) // @Param limit query int false "Page limit" // @Param offset query int false "Page offset" // @Param since query string false "Since timestamp" format(date-time) // @Success 200 {array} codersdk.WorkspaceBuild -// @Router /workspaces/{id}/builds [get] +// @Router /workspaces/{workspace}/builds [get] func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspace := httpmw.WorkspaceParam(r) @@ -293,10 +293,10 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ // @Accepts json // @Produce json // @Tags Builds -// @Param id path string true "Workspace ID" format(uuid) +// @Param workspace path string true "Workspace ID" format(uuid) // @Param request body codersdk.CreateWorkspaceBuildRequest true "Create workspace build request" // @Success 200 {object} codersdk.WorkspaceBuild -// @Router /workspaces/{id}/builds [post] +// @Router /workspaces/{workspace}/builds [post] func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 6ef629fe47bd0..987bc4e19dc68 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -51,7 +51,7 @@ var ( // @Param id path string true "Workspace ID" format(uuid) // @Param include_deleted query bool false "Return data instead of HTTP 404 if the workspace is deleted" // @Success 200 {object} codersdk.Workspace -// @Router /workspaces/{id} [get] +// @Router /workspaces/{workspace} [get] func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspace := httpmw.WorkspaceParam(r) diff --git a/docs/api/builds.md b/docs/api/builds.md index d69028c041b17..9cb3c9abcd6a4 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -740,22 +740,22 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/workspaces/{id}/builds \ +curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /workspaces/{id}/builds` +`GET /workspaces/{workspace}/builds` ### Parameters -| Name | In | Type | Required | Description | -| ---------- | ----- | ----------------- | -------- | --------------- | -| `id` | path | string(uuid) | true | Workspace ID | -| `after_id` | query | string(uuid) | false | After ID | -| `limit` | query | integer | false | Page limit | -| `offset` | query | integer | false | Page offset | -| `since` | query | string(date-time) | false | Since timestamp | +| Name | In | Type | Required | Description | +| ----------- | ----- | ----------------- | -------- | --------------- | +| `workspace` | path | string(uuid) | true | Workspace ID | +| `after_id` | query | string(uuid) | false | After ID | +| `limit` | query | integer | false | Page limit | +| `offset` | query | integer | false | Page offset | +| `since` | query | string(date-time) | false | Since timestamp | ### Example responses @@ -1019,13 +1019,13 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X POST http://coder-server:8080/api/v2/workspaces/{id}/builds \ +curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`POST /workspaces/{id}/builds` +`POST /workspaces/{workspace}/builds` > Body parameter @@ -1050,10 +1050,10 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{id}/builds \ ### Parameters -| Name | In | Type | Required | Description | -| ------ | ---- | -------------------------------------------------------------------------------------- | -------- | ------------------------------ | -| `id` | path | string(uuid) | true | Workspace ID | -| `body` | body | [codersdk.CreateWorkspaceBuildRequest](schemas.md#codersdkcreateworkspacebuildrequest) | true | Create workspace build request | +| Name | In | Type | Required | Description | +| ----------- | ---- | -------------------------------------------------------------------------------------- | -------- | ------------------------------ | +| `workspace` | path | string(uuid) | true | Workspace ID | +| `body` | body | [codersdk.CreateWorkspaceBuildRequest](schemas.md#codersdkcreateworkspacebuildrequest) | true | Create workspace build request | ### Example responses diff --git a/docs/api/templates.md b/docs/api/templates.md index dc29086ca6e39..5444447844114 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -630,18 +630,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/templates/{id} \ +curl -X GET http://coder-server:8080/api/v2/templates/{template} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /templates/{id}` +`GET /templates/{template}` ### Parameters -| Name | In | Type | Required | Description | -| ---- | ---- | ------------ | -------- | ----------- | -| `id` | path | string(uuid) | true | Template ID | +| Name | In | Type | Required | Description | +| ---------- | ---- | ------------ | -------- | ----------- | +| `template` | path | string(uuid) | true | Template ID | ### Example responses @@ -692,18 +692,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X DELETE http://coder-server:8080/api/v2/templates/{id} \ +curl -X DELETE http://coder-server:8080/api/v2/templates/{template} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`DELETE /templates/{id}` +`DELETE /templates/{template}` ### Parameters -| Name | In | Type | Required | Description | -| ---- | ---- | ------------ | -------- | ----------- | -| `id` | path | string(uuid) | true | Template ID | +| Name | In | Type | Required | Description | +| ---------- | ---- | ------------ | -------- | ----------- | +| `template` | path | string(uuid) | true | Template ID | ### Example responses @@ -736,18 +736,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/templates/{id} \ +curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PATCH /templates/{id}` +`PATCH /templates/{template}` ### Parameters -| Name | In | Type | Required | Description | -| ---- | ---- | ------------ | -------- | ----------- | -| `id` | path | string(uuid) | true | Template ID | +| Name | In | Type | Required | Description | +| ---------- | ---- | ------------ | -------- | ----------- | +| `template` | path | string(uuid) | true | Template ID | ### Example responses @@ -798,18 +798,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/templates/{id}/daus \ +curl -X GET http://coder-server:8080/api/v2/templates/{template}/daus \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /templates/{id}/daus` +`GET /templates/{template}/daus` ### Parameters -| Name | In | Type | Required | Description | -| ---- | ---- | ------------ | -------- | ----------- | -| `id` | path | string(uuid) | true | Template ID | +| Name | In | Type | Required | Description | +| ---------- | ---- | ------------ | -------- | ----------- | +| `template` | path | string(uuid) | true | Template ID | ### Example responses @@ -840,18 +840,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/templates/{id}/versions \ +curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /templates/{id}/versions` +`GET /templates/{template}/versions` ### Parameters | Name | In | Type | Required | Description | | ---------- | ----- | ------------ | -------- | ----------- | -| `id` | path | string(uuid) | true | Template ID | +| `template` | path | string(uuid) | true | Template ID | | `after_id` | query | string(uuid) | false | After ID | | `limit` | query | integer | false | Page limit | | `offset` | query | integer | false | Page offset | @@ -971,13 +971,13 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/templates/{id}/versions \ +curl -X PATCH http://coder-server:8080/api/v2/templates/{template}/versions \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PATCH /templates/{id}/versions` +`PATCH /templates/{template}/versions` > Body parameter @@ -989,10 +989,10 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{id}/versions \ ### Parameters -| Name | In | Type | Required | Description | -| ------ | ---- | -------------------------------------------------------------------------------------- | -------- | ------------------------- | -| `id` | path | string(uuid) | true | Template ID | -| `body` | body | [codersdk.UpdateActiveTemplateVersion](schemas.md#codersdkupdateactivetemplateversion) | true | Modified template version | +| Name | In | Type | Required | Description | +| ---------- | ---- | -------------------------------------------------------------------------------------- | -------- | ------------------------- | +| `template` | path | string(uuid) | true | Template ID | +| `body` | body | [codersdk.UpdateActiveTemplateVersion](schemas.md#codersdkupdateactivetemplateversion) | true | Modified template version | ### Example responses @@ -1025,18 +1025,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/templates/{id}/versions/{templateversionname} \ +curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions/{templateversionname} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /templates/{id}/versions/{templateversionname}` +`GET /templates/{template}/versions/{templateversionname}` ### Parameters | Name | In | Type | Required | Description | | --------------------- | ---- | ------------ | -------- | --------------------- | -| `id` | path | string(uuid) | true | Template ID | +| `template` | path | string(uuid) | true | Template ID | | `templateversionname` | path | string | true | Template version name | ### Example responses @@ -1340,19 +1340,19 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run/{jobid} \ +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run/{jobID} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /templateversions/{templateversion}/dry-run/{jobid}` +`GET /templateversions/{templateversion}/dry-run/{jobID}` ### Parameters | Name | In | Type | Required | Description | | ----------------- | ---- | ------------ | -------- | ------------------- | | `templateversion` | path | string(uuid) | true | Template version ID | -| `jobid` | path | string(uuid) | true | Job ID | +| `jobID` | path | string(uuid) | true | Job ID | ### Example responses @@ -1390,12 +1390,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run/{jobid}/cancel \ +curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run/{jobID}/cancel \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PATCH /templateversions/{templateversion}/dry-run/{jobid}/cancel` +`PATCH /templateversions/{templateversion}/dry-run/{jobID}/cancel` ### Parameters @@ -1434,19 +1434,19 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run/{jobid}/logs \ +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run/{jobID}/logs \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /templateversions/{templateversion}/dry-run/{jobid}/logs` +`GET /templateversions/{templateversion}/dry-run/{jobID}/logs` ### Parameters | Name | In | Type | Required | Description | | ----------------- | ----- | ------------ | -------- | --------------------- | | `templateversion` | path | string(uuid) | true | Template version ID | -| `jobid` | path | string(uuid) | true | Job ID | +| `job-ID` | path | string(uuid) | true | Job ID | | `before` | query | integer | false | Before Unix timestamp | | `after` | query | integer | false | After Unix timestamp | | `follow` | query | boolean | false | Follow log stream | @@ -1508,19 +1508,19 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run/{jobid}/resources \ +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/dry-run/{jobID}/resources \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /templateversions/{templateversion}/dry-run/{jobid}/resources` +`GET /templateversions/{templateversion}/dry-run/{jobID}/resources` ### Parameters | Name | In | Type | Required | Description | | ----------------- | ---- | ------------ | -------- | ------------------- | | `templateversion` | path | string(uuid) | true | Template version ID | -| `jobid` | path | string(uuid) | true | Job ID | +| `jobID` | path | string(uuid) | true | Job ID | ### Example responses diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index c97140e001257..1f7ce6ff805ec 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -511,12 +511,12 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/workspaces/{id} \ +curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /workspaces/{id}` +`GET /workspaces/{workspace}` ### Parameters From 33d93d672c8f271d65cc7d3f07ff421b65b85d4e Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 10:51:03 +0100 Subject: [PATCH 46/61] Fix: cover workspaceagents --- coderd/apidoc/docs.go | 212 +++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 192 ++++++++++++++++++++++++++ coderd/workspaceagents.go | 40 ++++++ docs/api/agents.md | 268 +++++++++++++++++++++++++++++++++++++ docs/api/schemas.md | 116 ++++++++++++++++ 5 files changed, 828 insertions(+) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index f868bedc069c1..bfac7dbf63530 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -3842,6 +3842,169 @@ const docTemplate = `{ } } }, + "/workspaceagents/{workspaceagent}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Agents" + ], + "summary": "Get workspace agent by ID", + "operationId": "get-workspace-agent-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace agent ID", + "name": "workspaceagent", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgent" + } + } + } + } + }, + "/workspaceagents/{workspaceagent}/connection": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Agents" + ], + "summary": "Get connection info for workspace agent", + "operationId": "get-connection-info-for-workspace-agent", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace agent ID", + "name": "workspaceagent", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentConnectionInfo" + } + } + } + } + }, + "/workspaceagents/{workspaceagent}/coordinate": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "tags": [ + "Agents" + ], + "summary": "Coordinate workspace agent", + "operationId": "coordinate-workspace-agent", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace agent ID", + "name": "workspaceagent", + "in": "path", + "required": true + } + ], + "responses": { + "101": { + "description": "Switching Protocols" + } + } + } + }, + "/workspaceagents/{workspaceagent}/listening-ports": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Agents" + ], + "summary": "Get listening ports for workspace agent", + "operationId": "get-listening-ports-for-workspace-agent", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace agent ID", + "name": "workspaceagent", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ListeningPortsResponse" + } + } + } + } + }, + "/workspaceagents/{workspaceagent}/pty": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "tags": [ + "Agents" + ], + "summary": "Open PTY to workspace agent", + "operationId": "open-pty-to-workspace agent", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace agent ID", + "name": "workspaceagent", + "in": "path", + "required": true + } + ], + "responses": { + "101": { + "description": "Switching Protocols" + } + } + } + }, "/workspacebuilds/{workspacebuild}": { "get": { "security": [ @@ -5851,6 +6014,47 @@ const docTemplate = `{ } } }, + "codersdk.ListeningPort": { + "type": "object", + "properties": { + "network": { + "description": "only \"tcp\" at the moment", + "allOf": [ + { + "$ref": "#/definitions/codersdk.ListeningPortNetwork" + } + ] + }, + "port": { + "type": "integer" + }, + "process_name": { + "description": "may be empty", + "type": "string" + } + } + }, + "codersdk.ListeningPortNetwork": { + "type": "string", + "enum": [ + "tcp" + ], + "x-enum-varnames": [ + "ListeningPortNetworkTCP" + ] + }, + "codersdk.ListeningPortsResponse": { + "type": "object", + "properties": { + "ports": { + "description": "If there are no ports in the list, nothing should be displayed in the UI.\nThere must not be a \"no ports available\" message or anything similar, as\nthere will always be no ports displayed on platforms where our port\ndetection logic is unsupported.", + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ListeningPort" + } + } + } + }, "codersdk.LogLevel": { "type": "string", "enum": [ @@ -7222,6 +7426,14 @@ const docTemplate = `{ } } }, + "codersdk.WorkspaceAgentConnectionInfo": { + "type": "object", + "properties": { + "derp_map": { + "$ref": "#/definitions/tailcfg.DERPMap" + } + } + }, "codersdk.WorkspaceAgentGitAuthResponse": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 0c2ae1220c77b..b29a687c032ea 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -3380,6 +3380,153 @@ } } }, + "/workspaceagents/{workspaceagent}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Agents"], + "summary": "Get workspace agent by ID", + "operationId": "get-workspace-agent-by-id", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace agent ID", + "name": "workspaceagent", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgent" + } + } + } + } + }, + "/workspaceagents/{workspaceagent}/connection": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Agents"], + "summary": "Get connection info for workspace agent", + "operationId": "get-connection-info-for-workspace-agent", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace agent ID", + "name": "workspaceagent", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.WorkspaceAgentConnectionInfo" + } + } + } + } + }, + "/workspaceagents/{workspaceagent}/coordinate": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "tags": ["Agents"], + "summary": "Coordinate workspace agent", + "operationId": "coordinate-workspace-agent", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace agent ID", + "name": "workspaceagent", + "in": "path", + "required": true + } + ], + "responses": { + "101": { + "description": "Switching Protocols" + } + } + } + }, + "/workspaceagents/{workspaceagent}/listening-ports": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Agents"], + "summary": "Get listening ports for workspace agent", + "operationId": "get-listening-ports-for-workspace-agent", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace agent ID", + "name": "workspaceagent", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.ListeningPortsResponse" + } + } + } + } + }, + "/workspaceagents/{workspaceagent}/pty": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "tags": ["Agents"], + "summary": "Open PTY to workspace agent", + "operationId": "open-pty-to-workspace agent", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace agent ID", + "name": "workspaceagent", + "in": "path", + "required": true + } + ], + "responses": { + "101": { + "description": "Switching Protocols" + } + } + } + }, "/workspacebuilds/{workspacebuild}": { "get": { "security": [ @@ -5240,6 +5387,43 @@ } } }, + "codersdk.ListeningPort": { + "type": "object", + "properties": { + "network": { + "description": "only \"tcp\" at the moment", + "allOf": [ + { + "$ref": "#/definitions/codersdk.ListeningPortNetwork" + } + ] + }, + "port": { + "type": "integer" + }, + "process_name": { + "description": "may be empty", + "type": "string" + } + } + }, + "codersdk.ListeningPortNetwork": { + "type": "string", + "enum": ["tcp"], + "x-enum-varnames": ["ListeningPortNetworkTCP"] + }, + "codersdk.ListeningPortsResponse": { + "type": "object", + "properties": { + "ports": { + "description": "If there are no ports in the list, nothing should be displayed in the UI.\nThere must not be a \"no ports available\" message or anything similar, as\nthere will always be no ports displayed on platforms where our port\ndetection logic is unsupported.", + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ListeningPort" + } + } + } + }, "codersdk.LogLevel": { "type": "string", "enum": ["trace", "debug", "info", "warn", "error"], @@ -6502,6 +6686,14 @@ } } }, + "codersdk.WorkspaceAgentConnectionInfo": { + "type": "object", + "properties": { + "derp_map": { + "$ref": "#/definitions/tailcfg.DERPMap" + } + } + }, "codersdk.WorkspaceAgentGitAuthResponse": { "type": "object", "properties": { diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 1464f3282f0cf..d0645d1495764 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -35,6 +35,14 @@ import ( "github.com/coder/coder/tailnet" ) +// @Summary Get workspace agent by ID +// @ID get-workspace-agent-by-id +// @Security CoderSessionToken +// @Produce json +// @Tags Agents +// @Param workspaceagent path string true "Workspace agent ID" format(uuid) +// @Success 200 {object} codersdk.WorkspaceAgent +// @Router /workspaceagents/{workspaceagent} [get] func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgentParam(r) @@ -198,6 +206,14 @@ func (api *API) postWorkspaceAgentVersion(rw http.ResponseWriter, r *http.Reques // workspaceAgentPTY spawns a PTY and pipes it over a WebSocket. // This is used for the web terminal. +// +// @Summary Open PTY to workspace agent +// @ID open-pty-to-workspace agent +// @Security CoderSessionToken +// @Tags Agents +// @Param workspaceagent path string true "Workspace agent ID" format(uuid) +// @Success 101 +// @Router /workspaceagents/{workspaceagent}/pty [get] func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -276,6 +292,14 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) { agent.Bicopy(ctx, wsNetConn, ptNetConn) } +// @Summary Get listening ports for workspace agent +// @ID get-listening-ports-for-workspace-agent +// @Security CoderSessionToken +// @Produce json +// @Tags Agents +// @Param workspaceagent path string true "Workspace agent ID" format(uuid) +// @Success 200 {object} codersdk.ListeningPortsResponse +// @Router /workspaceagents/{workspaceagent}/listening-ports [get] func (api *API) workspaceAgentListeningPorts(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspace := httpmw.WorkspaceParam(r) @@ -443,6 +467,14 @@ func (api *API) dialWorkspaceAgentTailnet(r *http.Request, agentID uuid.UUID) (* }, nil } +// @Summary Get connection info for workspace agent +// @ID get-connection-info-for-workspace-agent +// @Security CoderSessionToken +// @Produce json +// @Tags Agents +// @Param workspaceagent path string true "Workspace agent ID" format(uuid) +// @Success 200 {object} codersdk.WorkspaceAgentConnectionInfo +// @Router /workspaceagents/{workspaceagent}/connection [get] func (api *API) workspaceAgentConnection(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspace := httpmw.WorkspaceParam(r) @@ -622,6 +654,14 @@ func (api *API) workspaceAgentCoordinate(rw http.ResponseWriter, r *http.Request // workspaceAgentClientCoordinate accepts a WebSocket that reads node network updates. // After accept a PubSub starts listening for new connection node updates // which are written to the WebSocket. +// +// @Summary Coordinate workspace agent +// @ID coordinate-workspace-agent +// @Security CoderSessionToken +// @Tags Agents +// @Param workspaceagent path string true "Workspace agent ID" format(uuid) +// @Success 101 +// @Router /workspaceagents/{workspaceagent}/coordinate [get] func (api *API) workspaceAgentClientCoordinate(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/docs/api/agents.md b/docs/api/agents.md index dbbbe61c8d3ae..dce42c445b760 100644 --- a/docs/api/agents.md +++ b/docs/api/agents.md @@ -437,3 +437,271 @@ curl -X POST http://coder-server:8080/api/v2/workspaceagents/me/report-stats \ | 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.AgentStatsResponse](schemas.md#codersdkagentstatsresponse) | To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get workspace agent by ID + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspaceagents/{workspaceagent}` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------------ | -------- | ------------------ | +| `workspaceagent` | path | string(uuid) | true | Workspace agent ID | + +### Example responses + +> 200 Response + +```json +{ + "apps": [ + { + "command": "string", + "display_name": "string", + "external": true, + "health": "disabled", + "healthcheck": { + "interval": 0, + "threshold": 0, + "url": "string" + }, + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "sharing_level": "owner", + "slug": "string", + "subdomain": true, + "url": "string" + } + ], + "architecture": "string", + "connection_timeout_seconds": 0, + "created_at": "2019-08-24T14:15:22Z", + "directory": "string", + "disconnected_at": "2019-08-24T14:15:22Z", + "environment_variables": { + "property1": "string", + "property2": "string" + }, + "first_connected_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "instance_id": "string", + "last_connected_at": "2019-08-24T14:15:22Z", + "latency": { + "property1": { + "latency_ms": 0, + "preferred": true + }, + "property2": { + "latency_ms": 0, + "preferred": true + } + }, + "name": "string", + "operating_system": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", + "startup_script": "string", + "status": "connecting", + "troubleshooting_url": "string", + "updated_at": "2019-08-24T14:15:22Z", + "version": "string" +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgent](schemas.md#codersdkworkspaceagent) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get connection info for workspace agent + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/connection \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspaceagents/{workspaceagent}/connection` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------------ | -------- | ------------------ | +| `workspaceagent` | path | string(uuid) | true | Workspace agent ID | + +### Example responses + +> 200 Response + +```json +{ + "derp_map": { + "omitDefaultRegions": true, + "regions": { + "property1": { + "avoid": true, + "embeddedRelay": true, + "nodes": [ + { + "certName": "string", + "derpport": 0, + "forceHTTP": true, + "hostName": "string", + "insecureForTests": true, + "ipv4": "string", + "ipv6": "string", + "name": "string", + "regionID": 0, + "stunonly": true, + "stunport": 0, + "stuntestIP": "string" + } + ], + "regionCode": "string", + "regionID": 0, + "regionName": "string" + }, + "property2": { + "avoid": true, + "embeddedRelay": true, + "nodes": [ + { + "certName": "string", + "derpport": 0, + "forceHTTP": true, + "hostName": "string", + "insecureForTests": true, + "ipv4": "string", + "ipv6": "string", + "name": "string", + "regionID": 0, + "stunonly": true, + "stunport": 0, + "stuntestIP": "string" + } + ], + "regionCode": "string", + "regionID": 0, + "regionName": "string" + } + } + } +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspaceAgentConnectionInfo](schemas.md#codersdkworkspaceagentconnectioninfo) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Coordinate workspace agent + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/coordinate \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspaceagents/{workspaceagent}/coordinate` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------------ | -------- | ------------------ | +| `workspaceagent` | path | string(uuid) | true | Workspace agent ID | + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------------------ | ------------------- | ------ | +| 101 | [Switching Protocols](https://tools.ietf.org/html/rfc7231#section-6.2.2) | Switching Protocols | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Get listening ports for workspace agent + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/listening-ports \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspaceagents/{workspaceagent}/listening-ports` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------------ | -------- | ------------------ | +| `workspaceagent` | path | string(uuid) | true | Workspace agent ID | + +### Example responses + +> 200 Response + +```json +{ + "ports": [ + { + "network": "tcp", + "port": 0, + "process_name": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ListeningPortsResponse](schemas.md#codersdklisteningportsresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Open PTY to workspace agent + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/pty \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /workspaceagents/{workspaceagent}/pty` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------------- | ---- | ------------ | -------- | ------------------ | +| `workspaceagent` | path | string(uuid) | true | Workspace agent ID | + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------------------------ | ------------------- | ------ | +| 101 | [Switching Protocols](https://tools.ietf.org/html/rfc7231#section-6.2.2) | Switching Protocols | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/docs/api/schemas.md b/docs/api/schemas.md index ed546daf87ca8..a9459f2be5401 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2525,6 +2525,58 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `uploaded_at` | string | false | | | | `uuid` | string | false | | | +## codersdk.ListeningPort + +```json +{ + "network": "tcp", + "port": 0, + "process_name": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------------- | -------------------------------------------------------------- | -------- | ------------ | ------------------------ | +| `network` | [codersdk.ListeningPortNetwork](#codersdklisteningportnetwork) | false | | only "tcp" at the moment | +| `port` | integer | false | | | +| `process_name` | string | false | | may be empty | + +## codersdk.ListeningPortNetwork + +```json +"tcp" +``` + +### Properties + +#### Enumerated Values + +| Value | +| ----- | +| `tcp` | + +## codersdk.ListeningPortsResponse + +```json +{ + "ports": [ + { + "network": "tcp", + "port": 0, + "process_name": "string" + } + ] +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------- | --------------------------------------------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `ports` | array of [codersdk.ListeningPort](#codersdklisteningport) | false | | If there are no ports in the list, nothing should be displayed in the UI. There must not be a "no ports available" message or anything similar, as there will always be no ports displayed on platforms where our port detection logic is unsupported. | + ## codersdk.LogLevel ```json @@ -4533,6 +4585,70 @@ Parameter represents a set value for the scope. | --------------- | ------ | -------- | ------------ | ----------- | | `session_token` | string | false | | | +## codersdk.WorkspaceAgentConnectionInfo + +```json +{ + "derp_map": { + "omitDefaultRegions": true, + "regions": { + "property1": { + "avoid": true, + "embeddedRelay": true, + "nodes": [ + { + "certName": "string", + "derpport": 0, + "forceHTTP": true, + "hostName": "string", + "insecureForTests": true, + "ipv4": "string", + "ipv6": "string", + "name": "string", + "regionID": 0, + "stunonly": true, + "stunport": 0, + "stuntestIP": "string" + } + ], + "regionCode": "string", + "regionID": 0, + "regionName": "string" + }, + "property2": { + "avoid": true, + "embeddedRelay": true, + "nodes": [ + { + "certName": "string", + "derpport": 0, + "forceHTTP": true, + "hostName": "string", + "insecureForTests": true, + "ipv4": "string", + "ipv6": "string", + "name": "string", + "regionID": 0, + "stunonly": true, + "stunport": 0, + "stuntestIP": "string" + } + ], + "regionCode": "string", + "regionID": 0, + "regionName": "string" + } + } + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------- | ---------------------------------- | -------- | ------------ | ----------- | +| `derp_map` | [tailcfg.DERPMap](#tailcfgderpmap) | false | | | + ## codersdk.WorkspaceAgentGitAuthResponse ```json From 76701b8f222dfb2e2dc21378f4479ac25acd8d38 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 11:25:52 +0100 Subject: [PATCH 47/61] Assert: consistent ID and summary --- coderd/apidoc/docs.go | 36 +++++++++++++++---------------- coderd/apidoc/swagger.json | 36 +++++++++++++++---------------- coderd/apikey.go | 2 +- coderd/audit.go | 2 +- coderd/coderdtest/swagger_test.go | 25 +++++++++++++++++++++ coderd/files.go | 2 +- coderd/templates.go | 2 +- coderd/templateversions.go | 4 ++-- coderd/userauth.go | 4 ++-- coderd/users.go | 2 +- coderd/workspaceagents.go | 12 +++++------ coderd/workspaceapps.go | 2 +- coderd/workspaces.go | 4 ++-- docs/api/agents.md | 2 +- 14 files changed, 80 insertions(+), 55 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index bfac7dbf63530..85a5e81709a38 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -145,7 +145,7 @@ const docTemplate = `{ "Applications" ], "summary": "Get applications host", - "operationId": "get-app-host", + "operationId": "get-applications-host", "responses": { "200": { "description": "OK", @@ -223,7 +223,7 @@ const docTemplate = `{ "Audit" ], "summary": "Generate fake audit log", - "operationId": "generate-fake-audit-logs", + "operationId": "generate-fake-audit-log", "parameters": [ { "description": "Audit log request", @@ -405,7 +405,7 @@ const docTemplate = `{ "Files" ], "summary": "Upload file", - "operationId": "update-file", + "operationId": "upload-file", "parameters": [ { "type": "string", @@ -1656,7 +1656,7 @@ const docTemplate = `{ "Templates" ], "summary": "Update template metadata by ID", - "operationId": "update-template-metadata", + "operationId": "update-template-metadata-by-id", "parameters": [ { "type": "string", @@ -1806,7 +1806,7 @@ const docTemplate = `{ "Templates" ], "summary": "List template versions by template ID", - "operationId": "list-template-versions-by-template-ID", + "operationId": "list-template-versions-by-template-id", "parameters": [ { "type": "string", @@ -1864,7 +1864,7 @@ const docTemplate = `{ "Templates" ], "summary": "Update active template version by template ID", - "operationId": "update-active-template-version-by-template-ID", + "operationId": "update-active-template-version-by-template-id", "parameters": [ { "description": "Modified template version", @@ -2688,7 +2688,7 @@ const docTemplate = `{ "Users" ], "summary": "OAuth 2.0 GitHub Callback", - "operationId": "oauth2-github-callback", + "operationId": "oauth-20-github-callback", "responses": { "307": { "description": "Temporary Redirect" @@ -2710,7 +2710,7 @@ const docTemplate = `{ "Users" ], "summary": "OpenID Connect Callback", - "operationId": "oidc-callback", + "operationId": "openid-connect-callback", "responses": { "307": { "description": "Temporary Redirect" @@ -3044,7 +3044,7 @@ const docTemplate = `{ "Users" ], "summary": "Delete API key", - "operationId": "delete-user-tokens", + "operationId": "delete-api-key", "parameters": [ { "type": "string", @@ -3083,7 +3083,7 @@ const docTemplate = `{ "Users" ], "summary": "Get organizations by user", - "operationId": "get-organizations-by-users", + "operationId": "get-organizations-by-user", "parameters": [ { "type": "string", @@ -3627,8 +3627,8 @@ const docTemplate = `{ "tags": [ "Agents" ], - "summary": "Submit workspace application health", - "operationId": "submit-workspace-workspace-agent-health", + "summary": "Submit workspace agent application health", + "operationId": "submit-workspace-agent-application-health", "parameters": [ { "description": "Application health request", @@ -3662,7 +3662,7 @@ const docTemplate = `{ "Agents" ], "summary": "Coordinate workspace agent via Tailnet", - "operationId": "get-workspace-agent-git-ssh-key-via-tailnet", + "operationId": "coordinate-workspace-agent-via-tailnet", "responses": { "101": { "description": "Switching Protocols" @@ -3784,7 +3784,7 @@ const docTemplate = `{ "Agents" ], "summary": "Submit workspace agent stats", - "operationId": "submit-workspace-workspace-agent-stats", + "operationId": "submit-workspace-agent-stats", "parameters": [ { "description": "Stats request", @@ -3820,7 +3820,7 @@ const docTemplate = `{ "Agents" ], "summary": "Submit workspace agent version", - "operationId": "submit-workspace-workspace-agent-version", + "operationId": "submit-workspace-agent-version", "parameters": [ { "description": "Version request", @@ -3987,7 +3987,7 @@ const docTemplate = `{ "Agents" ], "summary": "Open PTY to workspace agent", - "operationId": "open-pty-to-workspace agent", + "operationId": "open-pty-to-workspace-agent", "parameters": [ { "type": "string", @@ -4213,7 +4213,7 @@ const docTemplate = `{ "Workspaces" ], "summary": "List workspaces", - "operationId": "get-workspaces", + "operationId": "list-workspaces", "parameters": [ { "type": "string", @@ -4611,7 +4611,7 @@ const docTemplate = `{ "Workspaces" ], "summary": "Watch workspace by ID", - "operationId": "watch-workspace-id", + "operationId": "watch-workspace-by-id", "parameters": [ { "type": "string", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index b29a687c032ea..d0150000f71f1 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -119,7 +119,7 @@ "produces": ["application/json"], "tags": ["Applications"], "summary": "Get applications host", - "operationId": "get-app-host", + "operationId": "get-applications-host", "responses": { "200": { "description": "OK", @@ -189,7 +189,7 @@ "consumes": ["application/json"], "tags": ["Audit"], "summary": "Generate fake audit log", - "operationId": "generate-fake-audit-logs", + "operationId": "generate-fake-audit-log", "parameters": [ { "description": "Audit log request", @@ -341,7 +341,7 @@ "produces": ["application/json"], "tags": ["Files"], "summary": "Upload file", - "operationId": "update-file", + "operationId": "upload-file", "parameters": [ { "type": "string", @@ -1444,7 +1444,7 @@ "produces": ["application/json"], "tags": ["Templates"], "summary": "Update template metadata by ID", - "operationId": "update-template-metadata", + "operationId": "update-template-metadata-by-id", "parameters": [ { "type": "string", @@ -1578,7 +1578,7 @@ "produces": ["application/json"], "tags": ["Templates"], "summary": "List template versions by template ID", - "operationId": "list-template-versions-by-template-ID", + "operationId": "list-template-versions-by-template-id", "parameters": [ { "type": "string", @@ -1630,7 +1630,7 @@ "produces": ["application/json"], "tags": ["Templates"], "summary": "Update active template version by template ID", - "operationId": "update-active-template-version-by-template-ID", + "operationId": "update-active-template-version-by-template-id", "parameters": [ { "description": "Modified template version", @@ -2362,7 +2362,7 @@ "produces": ["application/json"], "tags": ["Users"], "summary": "OAuth 2.0 GitHub Callback", - "operationId": "oauth2-github-callback", + "operationId": "oauth-20-github-callback", "responses": { "307": { "description": "Temporary Redirect" @@ -2380,7 +2380,7 @@ "produces": ["application/json"], "tags": ["Users"], "summary": "OpenID Connect Callback", - "operationId": "oidc-callback", + "operationId": "openid-connect-callback", "responses": { "307": { "description": "Temporary Redirect" @@ -2674,7 +2674,7 @@ "produces": ["application/json"], "tags": ["Users"], "summary": "Delete API key", - "operationId": "delete-user-tokens", + "operationId": "delete-api-key", "parameters": [ { "type": "string", @@ -2709,7 +2709,7 @@ "produces": ["application/json"], "tags": ["Users"], "summary": "Get organizations by user", - "operationId": "get-organizations-by-users", + "operationId": "get-organizations-by-user", "parameters": [ { "type": "string", @@ -3195,8 +3195,8 @@ ], "produces": ["application/json"], "tags": ["Agents"], - "summary": "Submit workspace application health", - "operationId": "submit-workspace-workspace-agent-health", + "summary": "Submit workspace agent application health", + "operationId": "submit-workspace-agent-application-health", "parameters": [ { "description": "Application health request", @@ -3226,7 +3226,7 @@ "produces": ["application/json"], "tags": ["Agents"], "summary": "Coordinate workspace agent via Tailnet", - "operationId": "get-workspace-agent-git-ssh-key-via-tailnet", + "operationId": "coordinate-workspace-agent-via-tailnet", "responses": { "101": { "description": "Switching Protocols" @@ -3326,7 +3326,7 @@ "produces": ["application/json"], "tags": ["Agents"], "summary": "Submit workspace agent stats", - "operationId": "submit-workspace-workspace-agent-stats", + "operationId": "submit-workspace-agent-stats", "parameters": [ { "description": "Stats request", @@ -3358,7 +3358,7 @@ "produces": ["application/json"], "tags": ["Agents"], "summary": "Submit workspace agent version", - "operationId": "submit-workspace-workspace-agent-version", + "operationId": "submit-workspace-agent-version", "parameters": [ { "description": "Version request", @@ -3509,7 +3509,7 @@ ], "tags": ["Agents"], "summary": "Open PTY to workspace agent", - "operationId": "open-pty-to-workspace agent", + "operationId": "open-pty-to-workspace-agent", "parameters": [ { "type": "string", @@ -3711,7 +3711,7 @@ "produces": ["application/json"], "tags": ["Workspaces"], "summary": "List workspaces", - "operationId": "get-workspaces", + "operationId": "list-workspaces", "parameters": [ { "type": "string", @@ -4064,7 +4064,7 @@ "produces": ["text/event-stream"], "tags": ["Workspaces"], "summary": "Watch workspace by ID", - "operationId": "watch-workspace-id", + "operationId": "watch-workspace-by-id", "parameters": [ { "type": "string", diff --git a/coderd/apikey.go b/coderd/apikey.go index e4cbd7552c2cc..629154510650e 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -209,7 +209,7 @@ func (api *API) tokens(rw http.ResponseWriter, r *http.Request) { } // @Summary Delete API key -// @ID delete-user-tokens +// @ID delete-api-key // @Security CoderSessionToken // @Produce json // @Tags Users diff --git a/coderd/audit.go b/coderd/audit.go index 72b27690fb179..03f594f2f69d3 100644 --- a/coderd/audit.go +++ b/coderd/audit.go @@ -89,7 +89,7 @@ func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) { } // @Summary Generate fake audit log -// @ID generate-fake-audit-logs +// @ID generate-fake-audit-log // @Security CoderSessionToken // @Accept json // @Tags Audit diff --git a/coderd/coderdtest/swagger_test.go b/coderd/coderdtest/swagger_test.go index c0ed7694c5373..905ce2be0b799 100644 --- a/coderd/coderdtest/swagger_test.go +++ b/coderd/coderdtest/swagger_test.go @@ -5,6 +5,7 @@ import ( "go/parser" "go/token" "net/http" + "regexp" "strings" "testing" @@ -15,6 +16,9 @@ import ( ) type swaggerComment struct { + summary string + id string + method string router string } @@ -35,6 +39,11 @@ func TestAllEndpointsDocumented(t *testing.T) { c := findSwaggerCommentByMethodAndRoute(swaggerComments, method, route) assert.NotNil(t, c, "Missing @Router annotation for: [%s] %s", method, route) + if c == nil { + return nil // do not fail next assertion for this route + } + + assertConsistencyBetweenRouteIDAndSummary(t, *c) return nil }) } @@ -80,6 +89,12 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) swaggerComment { args := strings.SplitN(text, " ", 4) c.router = args[2] c.method = args[3][1 : len(args[3])-1] + } else if strings.Contains(text, "@Summary ") { + args := strings.SplitN(text, " ", 3) + c.summary = args[2] + } else if strings.Contains(text, "@ID ") { + args := strings.SplitN(text, " ", 3) + c.id = args[2] } } return c @@ -93,3 +108,13 @@ func findSwaggerCommentByMethodAndRoute(comments []swaggerComment, method, route } return nil } + +var nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9-]+`) + +func assertConsistencyBetweenRouteIDAndSummary(t *testing.T, comment swaggerComment) { + exp := strings.ToLower(comment.summary) + exp = strings.ReplaceAll(exp, " ", "-") + exp = nonAlphanumericRegex.ReplaceAllString(exp, "") + + assert.Equal(t, exp, comment.id, "Router ID must match summary") +} diff --git a/coderd/files.go b/coderd/files.go index c128d1bd6ed69..bf8a98f6054f3 100644 --- a/coderd/files.go +++ b/coderd/files.go @@ -25,7 +25,7 @@ const ( // @Summary Upload file // @Description Swagger notice: Swagger 2.0 doesn't support file upload with a `content-type` different than `application/x-www-form-urlencoded`. -// @ID update-file +// @ID upload-file // @Security CoderSessionToken // @Produce json // @Accept application/x-tar diff --git a/coderd/templates.go b/coderd/templates.go index 29e7e1b8c0b6d..8db8fcefdf92f 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -461,7 +461,7 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re } // @Summary Update template metadata by ID -// @ID update-template-metadata +// @ID update-template-metadata-by-id // @Security CoderSessionToken // @Produce json // @Tags Templates diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 195a6c0d5916f..2b9596a44913e 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -536,7 +536,7 @@ func (api *API) fetchTemplateVersionDryRunJob(rw http.ResponseWriter, r *http.Re } // @Summary List template versions by template ID -// @ID list-template-versions-by-template-ID +// @ID list-template-versions-by-template-id // @Security CoderSessionToken // @Produce json // @Tags Templates @@ -828,7 +828,7 @@ func (api *API) previousTemplateVersionByOrganizationAndName(rw http.ResponseWri } // @Summary Update active template version by template ID -// @ID update-active-template-version-by-template-ID +// @ID update-active-template-version-by-template-id // @Security CoderSessionToken // @Accept json // @Produce json diff --git a/coderd/userauth.go b/coderd/userauth.go index e1ee92d0eb822..48f7a452eb19d 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -59,7 +59,7 @@ func (api *API) userAuthMethods(rw http.ResponseWriter, r *http.Request) { } // @Summary OAuth 2.0 GitHub Callback -// @ID oauth2-github-callback +// @ID oauth-20-github-callback // @Security CoderSessionToken // @Produce json // @Tags Users @@ -218,7 +218,7 @@ type OIDCConfig struct { } // @Summary OpenID Connect Callback -// @ID oidc-callback +// @ID openid-connect-callback // @Security CoderSessionToken // @Produce json // @Tags Users diff --git a/coderd/users.go b/coderd/users.go index 0485a1e3ec1ed..3622580b71ce7 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -908,7 +908,7 @@ func (api *API) updateSiteUserRoles(ctx context.Context, args database.UpdateUse // Returns organizations the parameterized user has access to. // // @Summary Get organizations by user -// @ID get-organizations-by-users +// @ID get-organizations-by-user // @Security CoderSessionToken // @Produce json // @Tags Users diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index d0645d1495764..2389c63c9411a 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -155,7 +155,7 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) } // @Summary Submit workspace agent version -// @ID submit-workspace-workspace-agent-version +// @ID submit-workspace-agent-version // @Security CoderSessionToken // @Produce application/json // @Tags Agents @@ -208,7 +208,7 @@ func (api *API) postWorkspaceAgentVersion(rw http.ResponseWriter, r *http.Reques // This is used for the web terminal. // // @Summary Open PTY to workspace agent -// @ID open-pty-to-workspace agent +// @ID open-pty-to-workspace-agent // @Security CoderSessionToken // @Tags Agents // @Param workspaceagent path string true "Workspace agent ID" format(uuid) @@ -490,7 +490,7 @@ func (api *API) workspaceAgentConnection(rw http.ResponseWriter, r *http.Request // @Summary Coordinate workspace agent via Tailnet // @Description It accepts a WebSocket connection to an agent that listens to // @Description incoming connections and publishes node updates. -// @ID get-workspace-agent-git-ssh-key-via-tailnet +// @ID coordinate-workspace-agent-via-tailnet // @Security CoderSessionToken // @Produce json // @Tags Agents @@ -824,7 +824,7 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin } // @Summary Submit workspace agent stats -// @ID submit-workspace-workspace-agent-stats +// @ID submit-workspace-agent-stats // @Security CoderSessionToken // @Produce application/json // @Tags Agents @@ -900,8 +900,8 @@ func (api *API) workspaceAgentReportStats(rw http.ResponseWriter, r *http.Reques }) } -// @Summary Submit workspace application health -// @ID submit-workspace-workspace-agent-health +// @Summary Submit workspace agent application health +// @ID submit-workspace-agent-application-health // @Security CoderSessionToken // @Produce application/json // @Tags Agents diff --git a/coderd/workspaceapps.go b/coderd/workspaceapps.go index 20fdfefdb0492..e71efc74b41bb 100644 --- a/coderd/workspaceapps.go +++ b/coderd/workspaceapps.go @@ -66,7 +66,7 @@ var nonCanonicalHeaders = map[string]string{ } // @Summary Get applications host -// @ID get-app-host +// @ID get-applications-host // @Security CoderSessionToken // @Produce json // @Tags Applications diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 987bc4e19dc68..b5c6cad35170c 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -102,7 +102,7 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { } // @Summary List workspaces -// @ID get-workspaces +// @ID list-workspaces // @Security CoderSessionToken // @Produce json // @Tags Workspaces @@ -883,7 +883,7 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) { } // @Summary Watch workspace by ID -// @ID watch-workspace-id +// @ID watch-workspace-by-id // @Security CoderSessionToken // @Produce text/event-stream // @Tags Workspaces diff --git a/docs/api/agents.md b/docs/api/agents.md index dce42c445b760..e53421a6ee57b 100644 --- a/docs/api/agents.md +++ b/docs/api/agents.md @@ -142,7 +142,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaceagents/google-instance-ide To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Submit workspace application health +## Submit workspace agent application health ### Code samples From 716527673f32387fbf7825fb04db6e0686ac47e0 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 11:28:56 +0100 Subject: [PATCH 48/61] Assert: success or failure defined --- coderd/coderdtest/swagger_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/coderd/coderdtest/swagger_test.go b/coderd/coderdtest/swagger_test.go index 905ce2be0b799..b6e0517d7da69 100644 --- a/coderd/coderdtest/swagger_test.go +++ b/coderd/coderdtest/swagger_test.go @@ -21,6 +21,9 @@ type swaggerComment struct { method string router string + + hasSuccess bool + hasFailure bool } func TestAllEndpointsDocumented(t *testing.T) { @@ -44,6 +47,7 @@ func TestAllEndpointsDocumented(t *testing.T) { } assertConsistencyBetweenRouteIDAndSummary(t, *c) + assertSuccessOrFailureDefined(t, *c) return nil }) } @@ -95,6 +99,10 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) swaggerComment { } else if strings.Contains(text, "@ID ") { args := strings.SplitN(text, " ", 3) c.id = args[2] + } else if strings.Contains(text, "@Success ") { + c.hasSuccess = true + } else if strings.Contains(text, "@Failure ") { + c.hasFailure = true } } return c @@ -118,3 +126,7 @@ func assertConsistencyBetweenRouteIDAndSummary(t *testing.T, comment swaggerComm assert.Equal(t, exp, comment.id, "Router ID must match summary") } + +func assertSuccessOrFailureDefined(t *testing.T, comment swaggerComment) { + assert.True(t, comment.hasSuccess || comment.hasFailure, "At least one @Success or @Failure annotation must be defined") +} From 12e502aca4e0c1324c7c9c91e7bd6ac2b614ff19 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 11:39:41 +0100 Subject: [PATCH 49/61] Fix: parallel --- coderd/coderdtest/swagger_test.go | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/coderd/coderdtest/swagger_test.go b/coderd/coderdtest/swagger_test.go index b6e0517d7da69..7d8f9719b1d57 100644 --- a/coderd/coderdtest/swagger_test.go +++ b/coderd/coderdtest/swagger_test.go @@ -18,6 +18,7 @@ import ( type swaggerComment struct { summary string id string + tags string method string router string @@ -40,14 +41,19 @@ func TestAllEndpointsDocumented(t *testing.T) { route = route[:len(route)-1] } - c := findSwaggerCommentByMethodAndRoute(swaggerComments, method, route) - assert.NotNil(t, c, "Missing @Router annotation for: [%s] %s", method, route) - if c == nil { - return nil // do not fail next assertion for this route - } + t.Run(method+" "+route, func(t *testing.T) { + t.Parallel() + + c := findSwaggerCommentByMethodAndRoute(swaggerComments, method, route) + assert.NotNil(t, c, "Missing @Router annotation") + if c == nil { + return // do not fail next assertion for this route + } - assertConsistencyBetweenRouteIDAndSummary(t, *c) - assertSuccessOrFailureDefined(t, *c) + assertConsistencyBetweenRouteIDAndSummary(t, *c) + assertSuccessOrFailureDefined(t, *c) + assertRequiredAnnotations(t, *c) + }) return nil }) } @@ -103,6 +109,9 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) swaggerComment { c.hasSuccess = true } else if strings.Contains(text, "@Failure ") { c.hasFailure = true + } else if strings.Contains(text, "@Tags ") { + args := strings.SplitN(text, " ", 3) + c.tags = args[2] } } return c @@ -130,3 +139,9 @@ func assertConsistencyBetweenRouteIDAndSummary(t *testing.T, comment swaggerComm func assertSuccessOrFailureDefined(t *testing.T, comment swaggerComment) { assert.True(t, comment.hasSuccess || comment.hasFailure, "At least one @Success or @Failure annotation must be defined") } + +func assertRequiredAnnotations(t *testing.T, comment swaggerComment) { + assert.NotEmpty(t, comment.id, "@ID must be defined") + assert.NotEmpty(t, comment.summary, "@Summary must be defined") + assert.NotEmpty(t, comment.tags, "@Tags must be defined") +} From b5018227cec8fd83029f795d2be8ccd702e8766d Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 11:49:29 +0100 Subject: [PATCH 50/61] Refactor --- coderd/coderdtest/swagger_test.go | 35 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/coderd/coderdtest/swagger_test.go b/coderd/coderdtest/swagger_test.go index 7d8f9719b1d57..79e53aa853ad7 100644 --- a/coderd/coderdtest/swagger_test.go +++ b/coderd/coderdtest/swagger_test.go @@ -94,24 +94,29 @@ func parseSwaggerComments(dir string) ([]swaggerComment, error) { func parseSwaggerComment(commentGroup *ast.CommentGroup) swaggerComment { var c swaggerComment for _, line := range commentGroup.List { - text := strings.TrimSpace(line.Text) - if strings.Contains(text, "@Router ") { - args := strings.SplitN(text, " ", 4) - c.router = args[2] - c.method = args[3][1 : len(args[3])-1] - } else if strings.Contains(text, "@Summary ") { - args := strings.SplitN(text, " ", 3) - c.summary = args[2] - } else if strings.Contains(text, "@ID ") { + splitN := strings.SplitN(strings.TrimSpace(line.Text), " ", 2) + if len(splitN) < 2 { + continue // comment prefix without any content + } + text := splitN[1] // Skip the comment prefix (double-slash) + + if strings.HasPrefix(text, "@Router ") { args := strings.SplitN(text, " ", 3) - c.id = args[2] - } else if strings.Contains(text, "@Success ") { + c.router = args[1] + c.method = args[2][1 : len(args[2])-1] + } else if strings.HasPrefix(text, "@Summary ") { + args := strings.SplitN(text, " ", 2) + c.summary = args[1] + } else if strings.HasPrefix(text, "@ID ") { + args := strings.SplitN(text, " ", 2) + c.id = args[1] + } else if strings.HasPrefix(text, "@Success ") { c.hasSuccess = true - } else if strings.Contains(text, "@Failure ") { + } else if strings.HasPrefix(text, "@Failure ") { c.hasFailure = true - } else if strings.Contains(text, "@Tags ") { - args := strings.SplitN(text, " ", 3) - c.tags = args[2] + } else if strings.HasPrefix(text, "@Tags ") { + args := strings.SplitN(text, " ", 2) + c.tags = args[1] } } return c From 1d30352f893aa3f3eaedf589401f7c6c914d68ba Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 13:31:30 +0100 Subject: [PATCH 51/61] Support enterprise --- coderd/apidoc/docs.go | 128 ++++++++++- coderd/apidoc/swagger.json | 116 +++++++++- coderd/coderdtest/swagger_test.go | 147 +------------ coderd/coderdtest/swaggertest.go | 149 +++++++++++++ docs/api/enterprise.md | 206 ++++++++++++++++-- enterprise/coderd/coderd.go | 2 +- .../coderd/coderdenttest/swagger_test.go | 20 ++ enterprise/coderd/groups.go | 33 ++- enterprise/coderd/licenses.go | 2 +- 9 files changed, 623 insertions(+), 180 deletions(-) create mode 100644 coderd/coderdtest/swaggertest.go create mode 100644 enterprise/coderd/coderdenttest/swagger_test.go diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 85a5e81709a38..19b8187d7ccbb 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -500,7 +500,7 @@ const docTemplate = `{ } } }, - "/groups/{groupName}": { + "/groups/{group}": { "get": { "security": [ { @@ -519,7 +519,7 @@ const docTemplate = `{ { "type": "string", "description": "Group name", - "name": "groupName", + "name": "group", "in": "path", "required": true } @@ -532,9 +532,7 @@ const docTemplate = `{ } } } - } - }, - "/license/{id}": { + }, "delete": { "security": [ { @@ -547,21 +545,55 @@ const docTemplate = `{ "tags": [ "Enterprise" ], - "summary": "Delete license", - "operationId": "delete-license", + "summary": "Delete group by name", + "operationId": "delete-group-by-name", "parameters": [ { "type": "string", - "format": "number", - "description": "License ID", - "name": "id", + "description": "Group name", + "name": "group", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + }, + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Update group by name", + "operationId": "update-group-by-name", + "parameters": [ + { + "type": "string", + "description": "Group name", + "name": "group", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } } } } @@ -628,6 +660,38 @@ const docTemplate = `{ } } }, + "/licenses/{id}": { + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Delete license", + "operationId": "delete-license", + "parameters": [ + { + "type": "string", + "format": "number", + "description": "License ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/organizations": { "post": { "security": [ @@ -781,6 +845,48 @@ const docTemplate = `{ } } }, + "/organizations/{organization}/groups/{groupName}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Get group by organization and group name", + "operationId": "get-group-by-organization-and-group-name", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + } + }, "/organizations/{organization}/members/roles": { "get": { "security": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index d0150000f71f1..ec94c78134566 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -430,7 +430,7 @@ } } }, - "/groups/{groupName}": { + "/groups/{group}": { "get": { "security": [ { @@ -445,7 +445,7 @@ { "type": "string", "description": "Group name", - "name": "groupName", + "name": "group", "in": "path", "required": true } @@ -458,9 +458,7 @@ } } } - } - }, - "/license/{id}": { + }, "delete": { "security": [ { @@ -469,21 +467,51 @@ ], "produces": ["application/json"], "tags": ["Enterprise"], - "summary": "Delete license", - "operationId": "delete-license", + "summary": "Delete group by name", + "operationId": "delete-group-by-name", "parameters": [ { "type": "string", - "format": "number", - "description": "License ID", - "name": "id", + "description": "Group name", + "name": "group", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + }, + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Update group by name", + "operationId": "update-group-by-name", + "parameters": [ + { + "type": "string", + "description": "Group name", + "name": "group", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } } } } @@ -542,6 +570,34 @@ } } }, + "/licenses/{id}": { + "delete": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Delete license", + "operationId": "delete-license", + "parameters": [ + { + "type": "string", + "format": "number", + "description": "License ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/organizations": { "post": { "security": [ @@ -677,6 +733,44 @@ } } }, + "/organizations/{organization}/groups/{groupName}": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Get group by organization and group name", + "operationId": "get-group-by-organization-and-group-name", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Group" + } + } + } + } + }, "/organizations/{organization}/members/roles": { "get": { "security": [ diff --git a/coderd/coderdtest/swagger_test.go b/coderd/coderdtest/swagger_test.go index 79e53aa853ad7..b6154c5a73218 100644 --- a/coderd/coderdtest/swagger_test.go +++ b/coderd/coderdtest/swagger_test.go @@ -1,152 +1,19 @@ -package coderdtest +package coderdtest_test import ( - "go/ast" - "go/parser" - "go/token" - "net/http" - "regexp" - "strings" "testing" - "github.com/go-chi/chi/v5" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "golang.org/x/xerrors" -) - -type swaggerComment struct { - summary string - id string - tags string - method string - router string - - hasSuccess bool - hasFailure bool -} + "github.com/coder/coder/coderd/coderdtest" +) -func TestAllEndpointsDocumented(t *testing.T) { +func TestEndpointsDocumented(t *testing.T) { t.Parallel() - // TODO parse enterprise - swaggerComments, err := parseSwaggerComments("..") + swaggerComments, err := coderdtest.ParseSwaggerComments("..") require.NoError(t, err, "can't parse swagger comments") - _, _, api := NewWithAPI(t, nil) - chi.Walk(api.APIHandler, func(method, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { - method = strings.ToLower(method) - if route != "/" && strings.HasSuffix(route, "/") { - route = route[:len(route)-1] - } - - t.Run(method+" "+route, func(t *testing.T) { - t.Parallel() - - c := findSwaggerCommentByMethodAndRoute(swaggerComments, method, route) - assert.NotNil(t, c, "Missing @Router annotation") - if c == nil { - return // do not fail next assertion for this route - } - - assertConsistencyBetweenRouteIDAndSummary(t, *c) - assertSuccessOrFailureDefined(t, *c) - assertRequiredAnnotations(t, *c) - }) - return nil - }) -} - -func parseSwaggerComments(dir string) ([]swaggerComment, error) { - fileSet := token.NewFileSet() - commentNodes, err := parser.ParseDir(fileSet, dir, nil, parser.ParseComments) - if err != nil { - return nil, xerrors.Errorf(`parser.ParseDir failed "%s": %w`, dir, err) - } - - var swaggerComments []swaggerComment - for _, commentNode := range commentNodes { - ast.Inspect(commentNode, func(n ast.Node) bool { - commentGroup, ok := n.(*ast.CommentGroup) - if !ok { - return true - } - - var isSwaggerComment bool - for _, line := range commentGroup.List { - text := strings.TrimSpace(line.Text) - if strings.HasPrefix(text, "//") && strings.Contains(text, "@Router") { - isSwaggerComment = true - break - } - } - - if isSwaggerComment { - swaggerComments = append(swaggerComments, parseSwaggerComment(commentGroup)) - } - return true - }) - } - return swaggerComments, nil -} - -func parseSwaggerComment(commentGroup *ast.CommentGroup) swaggerComment { - var c swaggerComment - for _, line := range commentGroup.List { - splitN := strings.SplitN(strings.TrimSpace(line.Text), " ", 2) - if len(splitN) < 2 { - continue // comment prefix without any content - } - text := splitN[1] // Skip the comment prefix (double-slash) - - if strings.HasPrefix(text, "@Router ") { - args := strings.SplitN(text, " ", 3) - c.router = args[1] - c.method = args[2][1 : len(args[2])-1] - } else if strings.HasPrefix(text, "@Summary ") { - args := strings.SplitN(text, " ", 2) - c.summary = args[1] - } else if strings.HasPrefix(text, "@ID ") { - args := strings.SplitN(text, " ", 2) - c.id = args[1] - } else if strings.HasPrefix(text, "@Success ") { - c.hasSuccess = true - } else if strings.HasPrefix(text, "@Failure ") { - c.hasFailure = true - } else if strings.HasPrefix(text, "@Tags ") { - args := strings.SplitN(text, " ", 2) - c.tags = args[1] - } - } - return c -} - -func findSwaggerCommentByMethodAndRoute(comments []swaggerComment, method, route string) *swaggerComment { - for _, c := range comments { - if c.method == method && c.router == route { - return &c - } - } - return nil -} - -var nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9-]+`) - -func assertConsistencyBetweenRouteIDAndSummary(t *testing.T, comment swaggerComment) { - exp := strings.ToLower(comment.summary) - exp = strings.ReplaceAll(exp, " ", "-") - exp = nonAlphanumericRegex.ReplaceAllString(exp, "") - - assert.Equal(t, exp, comment.id, "Router ID must match summary") -} - -func assertSuccessOrFailureDefined(t *testing.T, comment swaggerComment) { - assert.True(t, comment.hasSuccess || comment.hasFailure, "At least one @Success or @Failure annotation must be defined") -} - -func assertRequiredAnnotations(t *testing.T, comment swaggerComment) { - assert.NotEmpty(t, comment.id, "@ID must be defined") - assert.NotEmpty(t, comment.summary, "@Summary must be defined") - assert.NotEmpty(t, comment.tags, "@Tags must be defined") + _, _, api := coderdtest.NewWithAPI(t, nil) + coderdtest.VerifySwaggerDefinitions(t, api.APIHandler, swaggerComments) } diff --git a/coderd/coderdtest/swaggertest.go b/coderd/coderdtest/swaggertest.go new file mode 100644 index 0000000000000..e294b2f50bcad --- /dev/null +++ b/coderd/coderdtest/swaggertest.go @@ -0,0 +1,149 @@ +package coderdtest + +import ( + "go/ast" + "go/parser" + "go/token" + "net/http" + "regexp" + "strings" + "testing" + + "github.com/go-chi/chi/v5" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/xerrors" +) + +type SwaggerComment struct { + summary string + id string + tags string + + method string + router string + + hasSuccess bool + hasFailure bool +} + +func ParseSwaggerComments(dirs ...string) ([]SwaggerComment, error) { + fileSet := token.NewFileSet() + + var swaggerComments []SwaggerComment + for _, dir := range dirs { + commentNodes, err := parser.ParseDir(fileSet, dir, nil, parser.ParseComments) + if err != nil { + return nil, xerrors.Errorf(`parser.ParseDir failed "%s": %w`, dir, err) + } + + for _, commentNode := range commentNodes { + ast.Inspect(commentNode, func(n ast.Node) bool { + commentGroup, ok := n.(*ast.CommentGroup) + if !ok { + return true + } + + var isSwaggerComment bool + for _, line := range commentGroup.List { + text := strings.TrimSpace(line.Text) + if strings.HasPrefix(text, "//") && strings.Contains(text, "@Router") { + isSwaggerComment = true + break + } + } + + if isSwaggerComment { + swaggerComments = append(swaggerComments, parseSwaggerComment(commentGroup)) + } + return true + }) + } + } + return swaggerComments, nil +} + +func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment { + var c SwaggerComment + for _, line := range commentGroup.List { + splitN := strings.SplitN(strings.TrimSpace(line.Text), " ", 2) + if len(splitN) < 2 { + continue // comment prefix without any content + } + text := splitN[1] // Skip the comment prefix (double-slash) + + if strings.HasPrefix(text, "@Router ") { + args := strings.SplitN(text, " ", 3) + c.router = args[1] + c.method = args[2][1 : len(args[2])-1] + } else if strings.HasPrefix(text, "@Summary ") { + args := strings.SplitN(text, " ", 2) + c.summary = args[1] + } else if strings.HasPrefix(text, "@ID ") { + args := strings.SplitN(text, " ", 2) + c.id = args[1] + } else if strings.HasPrefix(text, "@Success ") { + c.hasSuccess = true + } else if strings.HasPrefix(text, "@Failure ") { + c.hasFailure = true + } else if strings.HasPrefix(text, "@Tags ") { + args := strings.SplitN(text, " ", 2) + c.tags = args[1] + } + } + return c +} + +func VerifySwaggerDefinitions(t *testing.T, router chi.Router, swaggerComments []SwaggerComment) { + err := chi.Walk(router, func(method, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { + method = strings.ToLower(method) + if route != "/" && strings.HasSuffix(route, "/") { + route = route[:len(route)-1] + } + + t.Run(method+" "+route, func(t *testing.T) { + t.Parallel() + + c := findSwaggerCommentByMethodAndRoute(swaggerComments, method, route) + assert.NotNil(t, c, "Missing @Router annotation") + if c == nil { + return // do not fail next assertion for this route + } + + assertConsistencyBetweenRouteIDAndSummary(t, *c) + assertSuccessOrFailureDefined(t, *c) + assertRequiredAnnotations(t, *c) + }) + return nil + }) + require.NoError(t, err, "chi.Walk should not fail") +} + +func findSwaggerCommentByMethodAndRoute(comments []SwaggerComment, method, route string) *SwaggerComment { + for _, c := range comments { + if c.method == method && c.router == route { + return &c + } + } + return nil +} + +var nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9-]+`) + +func assertConsistencyBetweenRouteIDAndSummary(t *testing.T, comment SwaggerComment) { + exp := strings.ToLower(comment.summary) + exp = strings.ReplaceAll(exp, " ", "-") + exp = nonAlphanumericRegex.ReplaceAllString(exp, "") + + assert.Equal(t, exp, comment.id, "Router ID must match summary") +} + +func assertSuccessOrFailureDefined(t *testing.T, comment SwaggerComment) { + assert.True(t, comment.hasSuccess || comment.hasFailure, "At least one @Success or @Failure annotation must be defined") +} + +func assertRequiredAnnotations(t *testing.T, comment SwaggerComment) { + assert.NotEmpty(t, comment.id, "@ID must be defined") + assert.NotEmpty(t, comment.summary, "@Summary must be defined") + assert.NotEmpty(t, comment.tags, "@Tags must be defined") +} diff --git a/docs/api/enterprise.md b/docs/api/enterprise.md index b6df74df03153..85f775a2686a7 100644 --- a/docs/api/enterprise.md +++ b/docs/api/enterprise.md @@ -242,18 +242,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio ```shell # Example request using curl -curl -X GET http://coder-server:8080/api/v2/groups/{groupName} \ +curl -X GET http://coder-server:8080/api/v2/groups/{group} \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`GET /groups/{groupName}` +`GET /groups/{group}` ### Parameters -| Name | In | Type | Required | Description | -| ----------- | ---- | ------ | -------- | ----------- | -| `groupName` | path | string | true | Group name | +| Name | In | Type | Required | Description | +| ------- | ---- | ------ | -------- | ----------- | +| `group` | path | string | true | Group name | ### Example responses @@ -295,29 +295,121 @@ curl -X GET http://coder-server:8080/api/v2/groups/{groupName} \ To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Delete license +## Delete group by name ### Code samples ```shell # Example request using curl -curl -X DELETE http://coder-server:8080/api/v2/license/{id} \ +curl -X DELETE http://coder-server:8080/api/v2/groups/{group} \ + -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`DELETE /license/{id}` +`DELETE /groups/{group}` ### Parameters -| Name | In | Type | Required | Description | -| ---- | ---- | -------------- | -------- | ----------- | -| `id` | path | string(number) | true | License ID | +| Name | In | Type | Required | Description | +| ------- | ---- | ------ | -------- | ----------- | +| `group` | path | string | true | Group name | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "members": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + } + ], + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "quota_allowance": 0 +} +``` ### Responses -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ------ | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Group](schemas.md#codersdkgroup) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update group by name + +### Code samples + +```shell +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/groups/{group} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /groups/{group}` + +### Parameters + +| Name | In | Type | Required | Description | +| ------- | ---- | ------ | -------- | ----------- | +| `group` | path | string | true | Group name | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "members": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + } + ], + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "quota_allowance": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Group](schemas.md#codersdkgroup) | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -369,6 +461,32 @@ Status Code **200** To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Delete license + +### Code samples + +```shell +# Example request using curl +curl -X DELETE http://coder-server:8080/api/v2/licenses/{id} \ + -H 'Coder-Session-Token: API_KEY' +``` + +`DELETE /licenses/{id}` + +### Parameters + +| Name | In | Type | Required | Description | +| ---- | ---- | -------------- | -------- | ----------- | +| `id` | path | string(number) | true | License ID | + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Get groups by organization ### Code samples @@ -462,6 +580,66 @@ Status Code **200** To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Get group by organization and group name + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups/{groupName} \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /organizations/{organization}/groups/{groupName}` + +### Parameters + +| Name | In | Type | Required | Description | +| -------------- | ---- | ------------ | -------- | --------------- | +| `organization` | path | string(uuid) | true | Organization ID | +| `groupName` | path | string | true | Group name | + +### Example responses + +> 200 Response + +```json +{ + "avatar_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "members": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "organization_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], + "roles": [ + { + "display_name": "string", + "name": "string" + } + ], + "status": "active", + "username": "string" + } + ], + "name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "quota_allowance": 0 +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Group](schemas.md#codersdkgroup) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Get provisioner daemons ### Code samples diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 32184e43cdac1..4c0f9eb2a03b4 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -87,7 +87,7 @@ func New(ctx context.Context, options *Options) (*API, error) { httpmw.ExtractGroupByNameParam(api.Database), ) - r.Get("/", api.group) + r.Get("/", api.groupByOrganization) }) }) r.Route("/organizations/{organization}/provisionerdaemons", func(r chi.Router) { diff --git a/enterprise/coderd/coderdenttest/swagger_test.go b/enterprise/coderd/coderdenttest/swagger_test.go new file mode 100644 index 0000000000000..c4ad0a8dd5810 --- /dev/null +++ b/enterprise/coderd/coderdenttest/swagger_test.go @@ -0,0 +1,20 @@ +package coderdenttest_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/enterprise/coderd/coderdenttest" +) + +func TestEnterpriseEndpointsDocumented(t *testing.T) { + t.Parallel() + + swaggerComments, err := coderdtest.ParseSwaggerComments("..", "../../../coderd") + require.NoError(t, err, "can't parse swagger comments") + + _, _, api := coderdenttest.NewWithAPI(t, nil) + coderdtest.VerifySwaggerDefinitions(t, api.AGPL.APIHandler, swaggerComments) +} diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index ce0f92fc22339..288ae9483646c 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -80,6 +80,14 @@ func (api *API) postGroupByOrganization(rw http.ResponseWriter, r *http.Request) httpapi.Write(ctx, rw, http.StatusCreated, convertGroup(group, nil)) } +// @Summary Update group by name +// @ID update-group-by-name +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param group path string true "Group name" +// @Success 200 {object} codersdk.Group +// @Router /groups/{group} [patch] func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -236,6 +244,14 @@ func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, convertGroup(group, members)) } +// @Summary Delete group by name +// @ID delete-group-by-name +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param group path string true "Group name" +// @Success 200 {object} codersdk.Group +// @Router /groups/{group} [delete] func (api *API) deleteGroup(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -274,14 +290,27 @@ func (api *API) deleteGroup(rw http.ResponseWriter, r *http.Request) { }) } +// @Summary Get group by organization and group name +// @ID get-group-by-organization-and-group-name +// @Security CoderSessionToken +// @Produce json +// @Tags Enterprise +// @Param organization path string true "Organization ID" format(uuid) +// @Param groupName path string true "Group name" +// @Success 200 {object} codersdk.Group +// @Router /organizations/{organization}/groups/{groupName} [get] +func (api *API) groupByOrganization(rw http.ResponseWriter, r *http.Request) { + api.group(rw, r) +} + // @Summary Get group by name // @ID get-group-by-name // @Security CoderSessionToken // @Produce json // @Tags Enterprise -// @Param groupName path string true "Group name" +// @Param group path string true "Group name" // @Success 200 {object} codersdk.Group -// @Router /groups/{groupName} [get] +// @Router /groups/{group} [get] func (api *API) group(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/enterprise/coderd/licenses.go b/enterprise/coderd/licenses.go index 0ef3a564f9571..bee33db9de401 100644 --- a/enterprise/coderd/licenses.go +++ b/enterprise/coderd/licenses.go @@ -178,7 +178,7 @@ func (api *API) licenses(rw http.ResponseWriter, r *http.Request) { // @Tags Enterprise // @Param id path string true "License ID" format(number) // @Success 200 -// @Router /license/{id} [delete] +// @Router /licenses/{id} [delete] func (api *API) deleteLicense(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() if !api.AGPL.Authorize(r, rbac.ActionDelete, rbac.ResourceLicense) { From 9272c0126d7d6c148717f00bf3a9086d1886ce19 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 14:02:24 +0100 Subject: [PATCH 52/61] Go comment goes to top --- .../{swaggertest.go => swaggerparser.go} | 25 +++++++++++++++++- coderd/templates.go | 8 +++--- coderd/templateversions.go | 26 +++++++++---------- coderd/workspaceapps.go | 14 +++++----- coderd/workspaceresourceauth.go | 16 ++++++------ coderd/workspaces.go | 10 +++---- 6 files changed, 62 insertions(+), 37 deletions(-) rename coderd/coderdtest/{swaggertest.go => swaggerparser.go} (88%) diff --git a/coderd/coderdtest/swaggertest.go b/coderd/coderdtest/swaggerparser.go similarity index 88% rename from coderd/coderdtest/swaggertest.go rename to coderd/coderdtest/swaggerparser.go index e294b2f50bcad..1ca868dccdc80 100644 --- a/coderd/coderdtest/swaggertest.go +++ b/coderd/coderdtest/swaggerparser.go @@ -25,6 +25,8 @@ type SwaggerComment struct { hasSuccess bool hasFailure bool + + raw []*ast.Comment } func ParseSwaggerComments(dirs ...string) ([]SwaggerComment, error) { @@ -64,7 +66,9 @@ func ParseSwaggerComments(dirs ...string) ([]SwaggerComment, error) { } func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment { - var c SwaggerComment + c := SwaggerComment{ + raw: commentGroup.List, + } for _, line := range commentGroup.List { splitN := strings.SplitN(strings.TrimSpace(line.Text), " ", 2) if len(splitN) < 2 { @@ -113,6 +117,7 @@ func VerifySwaggerDefinitions(t *testing.T, router chi.Router, swaggerComments [ assertConsistencyBetweenRouteIDAndSummary(t, *c) assertSuccessOrFailureDefined(t, *c) assertRequiredAnnotations(t, *c) + assertGoCommentFirst(t, *c) }) return nil }) @@ -147,3 +152,21 @@ func assertRequiredAnnotations(t *testing.T, comment SwaggerComment) { assert.NotEmpty(t, comment.summary, "@Summary must be defined") assert.NotEmpty(t, comment.tags, "@Tags must be defined") } + +func assertGoCommentFirst(t *testing.T, comment SwaggerComment) { + var inSwaggerBlock bool + + for _, line := range comment.raw { + text := strings.TrimSpace(line.Text) + + if inSwaggerBlock { + if !strings.HasPrefix(text, "// @") { + assert.Fail(t, "Go function comment must be placed before swagger comments") + return + } + } + if strings.HasPrefix(text, "// @Summary") { + inSwaggerBlock = true + } + } +} diff --git a/coderd/templates.go b/coderd/templates.go index 8db8fcefdf92f..8b8296fdbed4b 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -23,6 +23,8 @@ import ( "github.com/coder/coder/examples" ) +// Returns a single template. +// // @Summary Get template metadata by ID // @ID get-template-metadata-by-id // @Security CoderSessionToken @@ -31,7 +33,6 @@ import ( // @Param template path string true "Template ID" format(uuid) // @Success 200 {object} codersdk.Template // @Router /templates/{template} [get] -// Returns a single template. func (api *API) template(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() template := httpmw.TemplateParam(r) @@ -131,6 +132,9 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) { }) } +// Create a new template in an organization. +// Returns a single template. +// // @Summary Create template by organization // @ID create-template-by-organization // @Security CoderSessionToken @@ -141,8 +145,6 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) { // @Param organization path string true "Organization ID" // @Success 200 {object} codersdk.Template // @Router /organizations/{organization}/templates [post] -// Returns a single template. -// Create a new template in an organization. func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 2b9596a44913e..f1ebffc07151c 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -911,6 +911,8 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque }) } +// postTemplateVersionsByOrganization creates a new version of a template. An import job is queued to parse the storage method provided. +// // @Summary Create template version by organization // @ID create-template-version-by-organization // @Security CoderSessionToken @@ -921,8 +923,6 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque // @Param request body codersdk.CreateTemplateVersionDryRunRequest true "Create template version request" // @Success 201 {object} codersdk.TemplateVersion // @Router /organizations/{organization}/templateversions [post] -// -// postTemplateVersionsByOrganization creates a new version of a template. An import job is queued to parse the storage method provided. func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -1207,6 +1207,12 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht httpapi.Write(ctx, rw, http.StatusCreated, convertTemplateVersion(templateVersion, convertProvisionerJob(provisionerJob), user)) } +// templateVersionResources returns the workspace agent resources associated +// with a template version. A template can specify more than one resource to be +// provisioned, each resource can have an agent that dials back to coderd. The +// agents returned are informative of the template version, and do not return +// agents associated with any particular workspace. +// // @Summary Get resources by template version // @ID get-resources-by-template-version // @Security CoderSessionToken @@ -1215,12 +1221,6 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht // @Param templateversion path string true "Template version ID" format(uuid) // @Success 200 {array} codersdk.WorkspaceResource // @Router /templateversions/{templateversion}/resources [get] -// -// templateVersionResources returns the workspace agent resources associated -// with a template version. A template can specify more than one resource to be -// provisioned, each resource can have an agent that dials back to coderd. The -// agents returned are informative of the template version, and do not return -// agents associated with any particular workspace. func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() @@ -1244,6 +1244,11 @@ func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request api.provisionerJobResources(rw, r, job) } +// templateVersionLogs returns the logs returned by the provisioner for the given +// template version. These logs are only associated with the template version, +// and not any build logs for a workspace. +// Eg: Logs returned from 'terraform plan' when uploading a new terraform file. +// // @Summary Get logs by template version // @ID get-logs-by-template-version // @Security CoderSessionToken @@ -1255,11 +1260,6 @@ func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request // @Param follow query bool false "Follow log stream" // @Success 200 {array} codersdk.ProvisionerJobLog // @Router /templateversions/{templateversion}/logs [get] -// -// templateVersionLogs returns the logs returned by the provisioner for the given -// template version. These logs are only associated with the template version, -// and not any build logs for a workspace. -// Eg: Logs returned from 'terraform plan' when uploading a new terraform file. func (api *API) templateVersionLogs(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/coderd/workspaceapps.go b/coderd/workspaceapps.go index e71efc74b41bb..fe6cd564dfd8d 100644 --- a/coderd/workspaceapps.go +++ b/coderd/workspaceapps.go @@ -614,6 +614,13 @@ func (api *API) setWorkspaceAppCookie(rw http.ResponseWriter, r *http.Request, t return true } +// workspaceApplicationAuth is an endpoint on the main router that handles +// redirects from the subdomain handler. +// +// This endpoint is under /api so we don't return the friendly error page here. +// Any errors on this endpoint should be errors that are unlikely to happen +// in production unless the user messes with the URL. +// // @Summary Redirect to URI with encrypted API key // @ID redirect-to-uri-with-encrypted-api-key // @Security CoderSessionToken @@ -621,13 +628,6 @@ func (api *API) setWorkspaceAppCookie(rw http.ResponseWriter, r *http.Request, t // @Param redirect_uri query string false "Redirect destination" // @Success 307 // @Router /applications/auth-redirect [get] -// -// workspaceApplicationAuth is an endpoint on the main router that handles -// redirects from the subdomain handler. -// -// This endpoint is under /api so we don't return the friendly error page here. -// Any errors on this endpoint should be errors that are unlikely to happen -// in production unless the user messes with the URL. func (api *API) workspaceApplicationAuth(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() if api.AppHostname == "" { diff --git a/coderd/workspaceresourceauth.go b/coderd/workspaceresourceauth.go index 8da31d1cdd96a..ea72b794ada37 100644 --- a/coderd/workspaceresourceauth.go +++ b/coderd/workspaceresourceauth.go @@ -45,6 +45,10 @@ func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r api.handleAuthInstanceID(rw, r, instanceID) } +// AWS supports instance identity verification: +// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html +// Using this, we can exchange a signed instance payload for an agent token. +// // @Summary Authenticate agent on AWS instance // @ID authenticate-agent-on-aws-instance // @Security CoderSessionToken @@ -53,10 +57,6 @@ func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r // @Param request body codersdk.AWSInstanceIdentityToken true "Instance identity token" // @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse // @Router /workspaceagents/aws-instance-identity [post] -// -// AWS supports instance identity verification: -// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html -// Using this, we can exchange a signed instance payload for an agent token. func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var req codersdk.AWSInstanceIdentityToken @@ -74,6 +74,10 @@ func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r * api.handleAuthInstanceID(rw, r, identity.InstanceID) } +// Google Compute Engine supports instance identity verification: +// https://cloud.google.com/compute/docs/instances/verifying-instance-identity +// Using this, we can exchange a signed instance payload for an agent token. +// // @Summary Authenticate agent on Google Cloud instance // @ID authenticate-agent-on-google-cloud-instance // @Security CoderSessionToken @@ -82,10 +86,6 @@ func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r * // @Param request body codersdk.GoogleInstanceIdentityToken true "Instance identity token" // @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse // @Router /workspaceagents/google-instance-identity [post] -// -// Google Compute Engine supports instance identity verification: -// https://cloud.google.com/compute/docs/instances/verifying-instance-identity -// Using this, we can exchange a signed instance payload for an agent token. func (api *API) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() var req codersdk.GoogleInstanceIdentityToken diff --git a/coderd/workspaces.go b/coderd/workspaces.go index b5c6cad35170c..34779ce5edab1 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -101,6 +101,9 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { )) } +// workspaces returns all workspaces a user can read. +// Optional filters with query params +// // @Summary List workspaces // @ID list-workspaces // @Security CoderSessionToken @@ -113,9 +116,6 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { // @Param has_agent query string false "Filter by agent status" Enums(connected,connecting,disconnected,timeout) // @Success 200 {object} codersdk.WorkspacesResponse // @Router /workspaces [get] -// -// workspaces returns all workspaces a user can read. -// Optional filters with query params func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() apiKey := httpmw.APIKey(r) @@ -266,6 +266,8 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) )) } +// Create a new workspace for the currently authenticated user. +// // @Summary Create user workspace by organization // @ID create-user-workspace-by-organization // @Security CoderSessionToken @@ -275,8 +277,6 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) // @Param user path string true "Username, UUID, or me" // @Success 200 {object} codersdk.Workspace // @Router /organizations/{organization}/members/{user}/workspaces [post] -// -// Create a new workspace for the currently authenticated user. func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() From 38b6dadda7e1be139212624663a376afbcedc826 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 14:17:51 +0100 Subject: [PATCH 53/61] Security --- coderd/coderdtest/swaggerparser.go | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 1ca868dccdc80..16d25144d2808 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -16,9 +16,10 @@ import ( ) type SwaggerComment struct { - summary string - id string - tags string + summary string + id string + security string + tags string method string router string @@ -93,6 +94,9 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment { } else if strings.HasPrefix(text, "@Tags ") { args := strings.SplitN(text, " ", 2) c.tags = args[1] + } else if strings.HasPrefix(text, "@Security ") { + args := strings.SplitN(text, " ", 2) + c.security = args[1] } } return c @@ -118,6 +122,8 @@ func VerifySwaggerDefinitions(t *testing.T, router chi.Router, swaggerComments [ assertSuccessOrFailureDefined(t, *c) assertRequiredAnnotations(t, *c) assertGoCommentFirst(t, *c) + assertURLParamsDefined(t, *c) + assertSecurityDefined(t, *c) }) return nil }) @@ -170,3 +176,15 @@ func assertGoCommentFirst(t *testing.T, comment SwaggerComment) { } } } + +func assertURLParamsDefined(t *testing.T, comment SwaggerComment) { +} + +func assertSecurityDefined(t *testing.T, comment SwaggerComment) { + if comment.router == "/updatecheck" || + comment.router == "/buildinfo" || + comment.router == "/" { + return // endpoints do not require authorization + } + assert.Equal(t, "CoderSessionToken", comment.security, "@Security must be equal CoderSessionToken") +} From a565c15c40335f1243a3f050aeca94b1131b8762 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 14:46:05 +0100 Subject: [PATCH 54/61] assertPathParametersDefined --- coderd/apidoc/docs.go | 12 +++++++-- coderd/apidoc/swagger.json | 12 +++++++-- coderd/coderdtest/swaggerparser.go | 41 +++++++++++++++++++++++++++--- coderd/templateversions.go | 3 ++- coderd/workspaces.go | 2 +- docs/api/templates.md | 3 ++- docs/api/workspaces.md | 2 +- 7 files changed, 64 insertions(+), 11 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 19b8187d7ccbb..8d4956ab491b4 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2224,6 +2224,14 @@ const docTemplate = `{ "summary": "Cancel template version dry-run by job ID", "operationId": "cancel-template-version-dry-run-by-job-id", "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Job ID", + "name": "jobID", + "in": "path", + "required": true + }, { "type": "string", "format": "uuid", @@ -2271,7 +2279,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Job ID", - "name": "job-ID", + "name": "jobID", "in": "path", "required": true }, @@ -4399,7 +4407,7 @@ const docTemplate = `{ "type": "string", "format": "uuid", "description": "Workspace ID", - "name": "id", + "name": "workspace", "in": "path", "required": true }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index ec94c78134566..4e98c4c9fa42d 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1950,6 +1950,14 @@ "summary": "Cancel template version dry-run by job ID", "operationId": "cancel-template-version-dry-run-by-job-id", "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Job ID", + "name": "jobID", + "in": "path", + "required": true + }, { "type": "string", "format": "uuid", @@ -1993,7 +2001,7 @@ "type": "string", "format": "uuid", "description": "Job ID", - "name": "job-ID", + "name": "jobID", "in": "path", "required": true }, @@ -3876,7 +3884,7 @@ "type": "string", "format": "uuid", "description": "Workspace ID", - "name": "id", + "name": "workspace", "in": "path", "required": true }, diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 16d25144d2808..2ac2719562ea0 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -27,9 +27,16 @@ type SwaggerComment struct { hasSuccess bool hasFailure bool + parameters []parameter + raw []*ast.Comment } +type parameter struct { + name string + kind string +} + func ParseSwaggerComments(dirs ...string) ([]SwaggerComment, error) { fileSet := token.NewFileSet() @@ -68,7 +75,8 @@ func ParseSwaggerComments(dirs ...string) ([]SwaggerComment, error) { func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment { c := SwaggerComment{ - raw: commentGroup.List, + raw: commentGroup.List, + parameters: []parameter{}, } for _, line := range commentGroup.List { splitN := strings.SplitN(strings.TrimSpace(line.Text), " ", 2) @@ -97,6 +105,13 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment { } else if strings.HasPrefix(text, "@Security ") { args := strings.SplitN(text, " ", 2) c.security = args[1] + } else if strings.HasPrefix(text, "@Param ") { + args := strings.SplitN(text, " ", 4) + p := parameter{ + name: args[1], + kind: args[2], + } + c.parameters = append(c.parameters, p) } } return c @@ -122,7 +137,7 @@ func VerifySwaggerDefinitions(t *testing.T, router chi.Router, swaggerComments [ assertSuccessOrFailureDefined(t, *c) assertRequiredAnnotations(t, *c) assertGoCommentFirst(t, *c) - assertURLParamsDefined(t, *c) + assertPathParametersDefined(t, *c) assertSecurityDefined(t, *c) }) return nil @@ -177,7 +192,27 @@ func assertGoCommentFirst(t *testing.T, comment SwaggerComment) { } } -func assertURLParamsDefined(t *testing.T, comment SwaggerComment) { +var urlParameterRegexp = regexp.MustCompile(`{[^{}]*}`) + +func assertPathParametersDefined(t *testing.T, comment SwaggerComment) { + matches := urlParameterRegexp.FindAllString(comment.router, -1) + if matches == nil { + return // router does not require any parameters + } + + for _, m := range matches { + var matched bool + for _, p := range comment.parameters { + if p.kind == "path" && "{"+p.name+"}" == m { + matched = true + break + } + } + + if !matched { + assert.Failf(t, "Missing @Param annotation", "Path parameter: %s", m) + } + } } func assertSecurityDefined(t *testing.T, comment SwaggerComment) { diff --git a/coderd/templateversions.go b/coderd/templateversions.go index f1ebffc07151c..a040f0315dcf0 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -394,7 +394,7 @@ func (api *API) templateVersionDryRunResources(rw http.ResponseWriter, r *http.R // @Produce json // @Tags Templates // @Param templateversion path string true "Template version ID" format(uuid) -// @Param job-ID path string true "Job ID" format(uuid) +// @Param jobID path string true "Job ID" format(uuid) // @Param before query int false "Before Unix timestamp" // @Param after query int false "After Unix timestamp" // @Param follow query bool false "Follow log stream" @@ -414,6 +414,7 @@ func (api *API) templateVersionDryRunLogs(rw http.ResponseWriter, r *http.Reques // @Security CoderSessionToken // @Produce json // @Tags Templates +// @Param jobID path string true "Job ID" format(uuid) // @Param templateversion path string true "Template version ID" format(uuid) // @Success 200 {object} codersdk.Response // @Router /templateversions/{templateversion}/dry-run/{jobID}/cancel [patch] diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 34779ce5edab1..6c9792c5a794a 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -48,7 +48,7 @@ var ( // @Security CoderSessionToken // @Produce json // @Tags Workspaces -// @Param id path string true "Workspace ID" format(uuid) +// @Param workspace path string true "Workspace ID" format(uuid) // @Param include_deleted query bool false "Return data instead of HTTP 404 if the workspace is deleted" // @Success 200 {object} codersdk.Workspace // @Router /workspaces/{workspace} [get] diff --git a/docs/api/templates.md b/docs/api/templates.md index 5444447844114..3d0ae02869189 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -1401,6 +1401,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversion} | Name | In | Type | Required | Description | | ----------------- | ---- | ------------ | -------- | ------------------- | +| `jobID` | path | string(uuid) | true | Job ID | | `templateversion` | path | string(uuid) | true | Template version ID | ### Example responses @@ -1446,7 +1447,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d | Name | In | Type | Required | Description | | ----------------- | ----- | ------------ | -------- | --------------------- | | `templateversion` | path | string(uuid) | true | Template version ID | -| `job-ID` | path | string(uuid) | true | Job ID | +| `jobID` | path | string(uuid) | true | Job ID | | `before` | query | integer | false | Before Unix timestamp | | `after` | query | integer | false | After Unix timestamp | | `follow` | query | boolean | false | Follow log stream | diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 1f7ce6ff805ec..7e12be69d76bf 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -522,7 +522,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ | Name | In | Type | Required | Description | | ----------------- | ----- | ------------ | -------- | ----------------------------------------------------------- | -| `id` | path | string(uuid) | true | Workspace ID | +| `workspace` | path | string(uuid) | true | Workspace ID | | `include_deleted` | query | boolean | false | Return data instead of HTTP 404 if the workspace is deleted | ### Example responses From 1bc855616645888fecee7760b305b9b903071b65 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 14:56:17 +0100 Subject: [PATCH 55/61] assertUniqueRoutes --- coderd/apidoc/docs.go | 20 ++++++++++++++++++++ coderd/apidoc/swagger.json | 16 ++++++++++++++++ coderd/coderdtest/swaggerparser.go | 15 +++++++++++++++ docs/api/enterprise.md | 20 ++++++++++++++++++++ enterprise/coderd/scim.go | 2 +- 5 files changed, 72 insertions(+), 1 deletion(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 8d4956ab491b4..cc2792308033d 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1573,6 +1573,26 @@ const docTemplate = `{ } }, "/scim/v2/Users": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/scim+json" + ], + "tags": [ + "Enterprise" + ], + "summary": "SCIM 2.0: Get users", + "operationId": "scim-get-users", + "responses": { + "200": { + "description": "OK" + } + } + }, "post": { "security": [ { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 4e98c4c9fa42d..ba503aa0018d5 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1373,6 +1373,22 @@ } }, "/scim/v2/Users": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/scim+json"], + "tags": ["Enterprise"], + "summary": "SCIM 2.0: Get users", + "operationId": "scim-get-users", + "responses": { + "200": { + "description": "OK" + } + } + }, "post": { "security": [ { diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 2ac2719562ea0..471cfaafb68cb 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -118,6 +118,8 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment { } func VerifySwaggerDefinitions(t *testing.T, router chi.Router, swaggerComments []SwaggerComment) { + assertUniqueRoutes(t, swaggerComments) + err := chi.Walk(router, func(method, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { method = strings.ToLower(method) if route != "/" && strings.HasSuffix(route, "/") { @@ -223,3 +225,16 @@ func assertSecurityDefined(t *testing.T, comment SwaggerComment) { } assert.Equal(t, "CoderSessionToken", comment.security, "@Security must be equal CoderSessionToken") } + +func assertUniqueRoutes(t *testing.T, comments []SwaggerComment) { + m := map[string]struct{}{} + + for _, c := range comments { + key := c.method + " " + c.router + _, alreadyDefined := m[key] + assert.False(t, alreadyDefined, "defined route must be unique (method: %s, route: %s)", c.method, c.router) + if !alreadyDefined { + m[key] = struct{}{} + } + } +} diff --git a/docs/api/enterprise.md b/docs/api/enterprise.md index 85f775a2686a7..55296663c51d5 100644 --- a/docs/api/enterprise.md +++ b/docs/api/enterprise.md @@ -787,6 +787,26 @@ Status Code **200** To perform this operation, you must be authenticated. [Learn more](authentication.md). +## SCIM 2.0: Get users + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/scim/v2/Users \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /scim/v2/Users` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## SCIM 2.0: Create new user ### Code samples diff --git a/enterprise/coderd/scim.go b/enterprise/coderd/scim.go index b72486fb28ea0..8912c3fafaf07 100644 --- a/enterprise/coderd/scim.go +++ b/enterprise/coderd/scim.go @@ -49,7 +49,7 @@ func (api *API) scimVerifyAuthHeader(r *http.Request) bool { // @Produce application/scim+json // @Tags Enterprise // @Success 200 -// @Router /scim/v2/Users [post] +// @Router /scim/v2/Users [get] // //nolint:revive func (api *API) scimGetUsers(rw http.ResponseWriter, r *http.Request) { From d0964f948501b853ad0fd13ec79a70bfbab26f98 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 15:24:30 +0100 Subject: [PATCH 56/61] assertRequestBody --- coderd/apidoc/docs.go | 57 +++++++++++++++++++++++------- coderd/apidoc/swagger.json | 19 +++++++--- coderd/apikey.go | 1 + coderd/coderdtest/swaggerparser.go | 51 +++++++++++++++++++++----- coderd/gitsshkey.go | 1 - coderd/organizations.go | 1 + coderd/templateversions.go | 1 - coderd/users.go | 3 ++ coderd/workspaceagents.go | 7 ++-- coderd/workspacebuilds.go | 2 +- coderd/workspaceresourceauth.go | 3 ++ enterprise/coderd/appearance.go | 1 + enterprise/coderd/licenses.go | 1 + enterprise/coderd/templates.go | 1 + 14 files changed, 118 insertions(+), 31 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index cc2792308033d..cafa215f401ef 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -75,6 +75,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -631,6 +634,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -699,6 +705,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -1846,6 +1855,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -2189,9 +2201,6 @@ const docTemplate = `{ "CoderSessionToken": [] } ], - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], @@ -2754,6 +2763,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -3088,6 +3100,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -3288,6 +3303,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -3328,6 +3346,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -3647,6 +3668,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -3683,6 +3707,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -3719,6 +3746,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -3755,6 +3785,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -3811,9 +3844,6 @@ const docTemplate = `{ "CoderSessionToken": [] } ], - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], @@ -3855,9 +3885,6 @@ const docTemplate = `{ "CoderSessionToken": [] } ], - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], @@ -3883,9 +3910,6 @@ const docTemplate = `{ "CoderSessionToken": [] } ], - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], @@ -3911,6 +3935,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -3947,6 +3974,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], @@ -4603,6 +4633,9 @@ const docTemplate = `{ "CoderSessionToken": [] } ], + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index ba503aa0018d5..96c05becb181d 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -59,6 +59,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Enterprise"], "summary": "Update appearance", @@ -545,6 +546,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Organizations"], "summary": "Add new license", @@ -605,6 +607,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Organizations"], "summary": "Create organization", @@ -1614,6 +1617,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Enterprise"], "summary": "Update template ACL", @@ -1921,7 +1925,6 @@ "CoderSessionToken": [] } ], - "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Templates"], "summary": "Get template version dry-run by job ID", @@ -2424,6 +2427,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Authorization"], "summary": "Log in user", @@ -2714,6 +2718,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Users"], "summary": "Create token API key", @@ -2894,6 +2899,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Users"], "summary": "Update user password", @@ -2930,6 +2936,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Users"], "summary": "Update user profile", @@ -3215,6 +3222,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Agents"], "summary": "Authenticate agent on AWS instance", @@ -3247,6 +3255,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Agents"], "summary": "Authenticate agent on Azure instance", @@ -3279,6 +3288,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Agents"], "summary": "Authenticate agent on Google Cloud instance", @@ -3311,6 +3321,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Agents"], "summary": "Submit workspace agent application health", @@ -3359,7 +3370,6 @@ "CoderSessionToken": [] } ], - "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Agents"], "summary": "Get workspace agent Git auth", @@ -3397,7 +3407,6 @@ "CoderSessionToken": [] } ], - "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Agents"], "summary": "Get workspace agent Git SSH key", @@ -3419,7 +3428,6 @@ "CoderSessionToken": [] } ], - "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Agents"], "summary": "Get authorized workspace agent metadata", @@ -3441,6 +3449,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Agents"], "summary": "Submit workspace agent stats", @@ -3473,6 +3482,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Agents"], "summary": "Submit workspace agent version", @@ -4060,6 +4070,7 @@ "CoderSessionToken": [] } ], + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Builds"], "summary": "Create workspace build", diff --git a/coderd/apikey.go b/coderd/apikey.go index 629154510650e..855bd89de96aa 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -29,6 +29,7 @@ import ( // @Summary Create token API key // @ID create-token-api-key // @Security CoderSessionToken +// @Accept json // @Produce json // @Tags Users // @Param user path string true "User ID, name, or me" diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 471cfaafb68cb..651c2c5c44106 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -20,6 +20,8 @@ type SwaggerComment struct { id string security string tags string + accept string + produce string method string router string @@ -112,6 +114,12 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment { kind: args[2], } c.parameters = append(c.parameters, p) + } else if strings.HasPrefix(text, "@Accept ") { + args := strings.SplitN(text, " ", 2) + c.accept = args[1] + } else if strings.HasPrefix(text, "@Produce ") { + args := strings.SplitN(text, " ", 2) + c.produce = args[1] } } return c @@ -141,12 +149,26 @@ func VerifySwaggerDefinitions(t *testing.T, router chi.Router, swaggerComments [ assertGoCommentFirst(t, *c) assertPathParametersDefined(t, *c) assertSecurityDefined(t, *c) + assertRequestBody(t, *c) }) return nil }) require.NoError(t, err, "chi.Walk should not fail") } +func assertUniqueRoutes(t *testing.T, comments []SwaggerComment) { + m := map[string]struct{}{} + + for _, c := range comments { + key := c.method + " " + c.router + _, alreadyDefined := m[key] + assert.False(t, alreadyDefined, "defined route must be unique (method: %s, route: %s)", c.method, c.router) + if !alreadyDefined { + m[key] = struct{}{} + } + } +} + func findSwaggerCommentByMethodAndRoute(comments []SwaggerComment, method, route string) *SwaggerComment { for _, c := range comments { if c.method == method && c.router == route { @@ -226,15 +248,26 @@ func assertSecurityDefined(t *testing.T, comment SwaggerComment) { assert.Equal(t, "CoderSessionToken", comment.security, "@Security must be equal CoderSessionToken") } -func assertUniqueRoutes(t *testing.T, comments []SwaggerComment) { - m := map[string]struct{}{} - - for _, c := range comments { - key := c.method + " " + c.router - _, alreadyDefined := m[key] - assert.False(t, alreadyDefined, "defined route must be unique (method: %s, route: %s)", c.method, c.router) - if !alreadyDefined { - m[key] = struct{}{} +func assertRequestBody(t *testing.T, comment SwaggerComment) { + var hasRequestBody bool + for _, c := range comment.parameters { + if c.name == "request" && c.kind == "body" || + c.name == "file" && c.kind == "formData" { + hasRequestBody = true + break } } + + var hasAccept bool + if comment.accept != "" { + hasAccept = true + } + + if comment.method == "get" { + assert.Empty(t, comment.accept, "GET route does not require the @Accept annotation") + assert.False(t, hasRequestBody, "GET route does not require the request body") + } else { + assert.False(t, hasRequestBody && !hasAccept, "Route with the request body requires the @Accept annotation") + assert.False(t, !hasRequestBody && hasAccept, "Route with @Accept annotation requires the request body or file formData parameter") + } } diff --git a/coderd/gitsshkey.go b/coderd/gitsshkey.go index 9e68d7a64283d..5e0dfcfef56f0 100644 --- a/coderd/gitsshkey.go +++ b/coderd/gitsshkey.go @@ -119,7 +119,6 @@ func (api *API) gitSSHKey(rw http.ResponseWriter, r *http.Request) { // @Summary Get workspace agent Git SSH key // @ID get-workspace-agent-git-ssh-key // @Security CoderSessionToken -// @Accept json // @Produce json // @Tags Agents // @Success 200 {object} codersdk.AgentGitSSHKey diff --git a/coderd/organizations.go b/coderd/organizations.go index 84bf1197348dd..7df9da184e351 100644 --- a/coderd/organizations.go +++ b/coderd/organizations.go @@ -40,6 +40,7 @@ func (api *API) organization(rw http.ResponseWriter, r *http.Request) { // @Summary Create organization // @ID create-organization // @Security CoderSessionToken +// @Accept json // @Produce json // @Tags Organizations // @Param request body codersdk.CreateOrganizationRequest true "Create organization request" diff --git a/coderd/templateversions.go b/coderd/templateversions.go index a040f0315dcf0..03aba85a3eb93 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -353,7 +353,6 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques // @Summary Get template version dry-run by job ID // @ID get-template-version-dry-run-by-job-id // @Security CoderSessionToken -// @Accept json // @Produce json // @Tags Templates // @Param templateversion path string true "Template version ID" format(uuid) diff --git a/coderd/users.go b/coderd/users.go index 3622580b71ce7..8aec601dad8c4 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -435,6 +435,7 @@ func (api *API) userByName(rw http.ResponseWriter, r *http.Request) { // @Summary Update user profile // @ID update-user-profile // @Security CoderSessionToken +// @Accept json // @Produce json // @Tags Users // @Param user path string true "User ID, name, or me" @@ -617,6 +618,7 @@ func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseW // @Summary Update user password // @ID update-user-password // @Security CoderSessionToken +// @Accept json // @Produce json // @Tags Users // @Param user path string true "User ID, name, or me" @@ -990,6 +992,7 @@ func (api *API) organizationByUserAndName(rw http.ResponseWriter, r *http.Reques // @Summary Log in user // @ID log-in-user // @Security CoderSessionToken +// @Accept json // @Produce json // @Tags Authorization // @Param request body codersdk.LoginWithPasswordRequest true "Login request" diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 2389c63c9411a..e53b84bc699b0 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -74,7 +74,6 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { // @Summary Get authorized workspace agent metadata // @ID get-authorized-workspace-agent-metadata // @Security CoderSessionToken -// @Accept json // @Produce json // @Tags Agents // @Success 200 {object} codersdk.WorkspaceAgentMetadata @@ -157,7 +156,8 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) // @Summary Submit workspace agent version // @ID submit-workspace-agent-version // @Security CoderSessionToken -// @Produce application/json +// @Accept json +// @Produce json // @Tags Agents // @Param request body codersdk.PostWorkspaceAgentVersionRequest true "Version request" // @Success 200 @@ -826,6 +826,7 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin // @Summary Submit workspace agent stats // @ID submit-workspace-agent-stats // @Security CoderSessionToken +// @Accept json // @Produce application/json // @Tags Agents // @Param request body codersdk.AgentStats true "Stats request" @@ -903,6 +904,7 @@ func (api *API) workspaceAgentReportStats(rw http.ResponseWriter, r *http.Reques // @Summary Submit workspace agent application health // @ID submit-workspace-agent-application-health // @Security CoderSessionToken +// @Accept json // @Produce application/json // @Tags Agents // @Param request body codersdk.PostWorkspaceAppHealthsRequest true "Application health request" @@ -1029,7 +1031,6 @@ func (api *API) postWorkspaceAppHealth(rw http.ResponseWriter, r *http.Request) // @Summary Get workspace agent Git auth // @ID get-workspace-agent-git-auth // @Security CoderSessionToken -// @Accept json // @Produce json // @Tags Agents // @Param url query string true "Git URL" format(uri) diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 6b65fa3d2985f..4c8fe6f60de7e 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -290,7 +290,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ // @Summary Create workspace build // @ID create-workspace-build // @Security CoderSessionToken -// @Accepts json +// @Accept json // @Produce json // @Tags Builds // @Param workspace path string true "Workspace ID" format(uuid) diff --git a/coderd/workspaceresourceauth.go b/coderd/workspaceresourceauth.go index ea72b794ada37..2ecc48a56a4c2 100644 --- a/coderd/workspaceresourceauth.go +++ b/coderd/workspaceresourceauth.go @@ -23,6 +23,7 @@ import ( // @Summary Authenticate agent on Azure instance // @ID authenticate-agent-on-azure-instance // @Security CoderSessionToken +// @Accept json // @Produce json // @Tags Agents // @Param request body codersdk.AzureInstanceIdentityToken true "Instance identity token" @@ -52,6 +53,7 @@ func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r // @Summary Authenticate agent on AWS instance // @ID authenticate-agent-on-aws-instance // @Security CoderSessionToken +// @Accept json // @Produce json // @Tags Agents // @Param request body codersdk.AWSInstanceIdentityToken true "Instance identity token" @@ -81,6 +83,7 @@ func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r * // @Summary Authenticate agent on Google Cloud instance // @ID authenticate-agent-on-google-cloud-instance // @Security CoderSessionToken +// @Accept json // @Produce json // @Tags Agents // @Param request body codersdk.GoogleInstanceIdentityToken true "Instance identity token" diff --git a/enterprise/coderd/appearance.go b/enterprise/coderd/appearance.go index fef1c2ff5ec63..e60e7a2ed5388 100644 --- a/enterprise/coderd/appearance.go +++ b/enterprise/coderd/appearance.go @@ -84,6 +84,7 @@ func validateHexColor(color string) error { // @Summary Update appearance // @ID update-appearance // @Security CoderSessionToken +// @Accept json // @Produce json // @Tags Enterprise // @Param request body codersdk.AppearanceConfig true "Update appearance request" diff --git a/enterprise/coderd/licenses.go b/enterprise/coderd/licenses.go index bee33db9de401..ef3f6c9d44882 100644 --- a/enterprise/coderd/licenses.go +++ b/enterprise/coderd/licenses.go @@ -52,6 +52,7 @@ var Keys = map[string]ed25519.PublicKey{"2022-08-12": ed25519.PublicKey(key20220 // @Summary Add new license // @ID add-new-license // @Security CoderSessionToken +// @Accept json // @Produce json // @Tags Organizations // @Param request body codersdk.AddLicenseRequest true "Add license request" diff --git a/enterprise/coderd/templates.go b/enterprise/coderd/templates.go index 397101c51fb1c..961ee09f0b5d0 100644 --- a/enterprise/coderd/templates.go +++ b/enterprise/coderd/templates.go @@ -103,6 +103,7 @@ func (api *API) templateACL(rw http.ResponseWriter, r *http.Request) { // @Summary Update template ACL // @ID update-template-acl // @Security CoderSessionToken +// @Accept json // @Produce json // @Tags Enterprise // @Param template path string true "Template ID" format(uuid) From b2d838f435ed2e6f63f73500d0c3890cebde4aed Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 16:05:56 +0100 Subject: [PATCH 57/61] More fixes --- coderd/apidoc/docs.go | 30 ------------ coderd/apidoc/swagger.json | 10 ---- coderd/apikey.go | 1 - coderd/coderdtest/swaggerparser.go | 63 ++++++++++++++++++++++--- coderd/csp.go | 1 - coderd/userauth.go | 2 - coderd/users.go | 1 - coderd/workspaceagents.go | 1 - coderd/workspaces.go | 3 -- enterprise/coderd/provisionerdaemons.go | 1 - 10 files changed, 56 insertions(+), 57 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index cafa215f401ef..b0942f16e8507 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -339,9 +339,6 @@ const docTemplate = `{ "consumes": [ "application/json" ], - "produces": [ - "text/plain" - ], "tags": [ "General" ], @@ -1074,9 +1071,6 @@ const docTemplate = `{ "CoderSessionToken": [] } ], - "produces": [ - "application/json" - ], "tags": [ "Enterprise" ], @@ -2827,9 +2821,6 @@ const docTemplate = `{ "CoderSessionToken": [] } ], - "produces": [ - "application/json" - ], "tags": [ "Users" ], @@ -2849,9 +2840,6 @@ const docTemplate = `{ "CoderSessionToken": [] } ], - "produces": [ - "application/json" - ], "tags": [ "Users" ], @@ -3186,9 +3174,6 @@ const docTemplate = `{ "CoderSessionToken": [] } ], - "produces": [ - "application/json" - ], "tags": [ "Users" ], @@ -3306,9 +3291,6 @@ const docTemplate = `{ "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ "Users" ], @@ -3822,9 +3804,6 @@ const docTemplate = `{ } ], "description": "It accepts a WebSocket connection to an agent that listens to\nincoming connections and publishes node updates.", - "produces": [ - "application/json" - ], "tags": [ "Agents" ], @@ -4486,9 +4465,6 @@ const docTemplate = `{ "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ "Workspaces" ], @@ -4530,9 +4506,6 @@ const docTemplate = `{ "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ "Workspaces" ], @@ -4730,9 +4703,6 @@ const docTemplate = `{ "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ "Workspaces" ], diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 96c05becb181d..4c9ee891d08c7 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -287,7 +287,6 @@ } ], "consumes": ["application/json"], - "produces": ["text/plain"], "tags": ["General"], "summary": "Report CSP violations", "operationId": "report-csp-violations", @@ -934,7 +933,6 @@ "CoderSessionToken": [] } ], - "produces": ["application/json"], "tags": ["Enterprise"], "summary": "Serve provisioner daemon", "operationId": "serve-provisioner-daemon", @@ -2481,7 +2479,6 @@ "CoderSessionToken": [] } ], - "produces": ["application/json"], "tags": ["Users"], "summary": "OAuth 2.0 GitHub Callback", "operationId": "oauth-20-github-callback", @@ -2499,7 +2496,6 @@ "CoderSessionToken": [] } ], - "produces": ["application/json"], "tags": ["Users"], "summary": "OpenID Connect Callback", "operationId": "openid-connect-callback", @@ -2794,7 +2790,6 @@ "CoderSessionToken": [] } ], - "produces": ["application/json"], "tags": ["Users"], "summary": "Delete API key", "operationId": "delete-api-key", @@ -2900,7 +2895,6 @@ } ], "consumes": ["application/json"], - "produces": ["application/json"], "tags": ["Users"], "summary": "Update user password", "operationId": "update-user-password", @@ -3352,7 +3346,6 @@ } ], "description": "It accepts a WebSocket connection to an agent that listens to\nincoming connections and publishes node updates.", - "produces": ["application/json"], "tags": ["Agents"], "summary": "Coordinate workspace agent via Tailnet", "operationId": "coordinate-workspace-agent-via-tailnet", @@ -3937,7 +3930,6 @@ } ], "consumes": ["application/json"], - "produces": ["application/json"], "tags": ["Workspaces"], "summary": "Update workspace metadata by ID", "operationId": "update-workspace-metadata-by-id", @@ -3975,7 +3967,6 @@ } ], "consumes": ["application/json"], - "produces": ["application/json"], "tags": ["Workspaces"], "summary": "Update workspace autostart schedule by ID", "operationId": "update-workspace-autostart-schedule-by-id", @@ -4153,7 +4144,6 @@ } ], "consumes": ["application/json"], - "produces": ["application/json"], "tags": ["Workspaces"], "summary": "Update workspace TTL by ID", "operationId": "update-workspace-ttl-by-id", diff --git a/coderd/apikey.go b/coderd/apikey.go index 855bd89de96aa..5e9aee7c58e66 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -212,7 +212,6 @@ func (api *API) tokens(rw http.ResponseWriter, r *http.Request) { // @Summary Delete API key // @ID delete-api-key // @Security CoderSessionToken -// @Produce json // @Tags Users // @Param user path string true "User ID, name, or me" // @Param keyid path string true "Key ID" format(uuid) diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 651c2c5c44106..319dbb54e0c76 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -26,8 +26,8 @@ type SwaggerComment struct { method string router string - hasSuccess bool - hasFailure bool + successes []response + failures []response parameters []parameter @@ -39,6 +39,12 @@ type parameter struct { kind string } +type response struct { + status string + kind string // {object} or {array} + model string +} + func ParseSwaggerComments(dirs ...string) ([]SwaggerComment, error) { fileSet := token.NewFileSet() @@ -79,6 +85,8 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment { c := SwaggerComment{ raw: commentGroup.List, parameters: []parameter{}, + successes: []response{}, + failures: []response{}, } for _, line := range commentGroup.List { splitN := strings.SplitN(strings.TrimSpace(line.Text), " ", 2) @@ -98,9 +106,33 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment { args := strings.SplitN(text, " ", 2) c.id = args[1] } else if strings.HasPrefix(text, "@Success ") { - c.hasSuccess = true + args := strings.Split(text, " ") + + var success response + if len(args) > 1 { + success.status = args[1] + } + if len(args) > 2 { + success.kind = args[2] + } + if len(args) > 3 { + success.model = args[3] + } + c.successes = append(c.successes, success) } else if strings.HasPrefix(text, "@Failure ") { - c.hasFailure = true + args := strings.Split(text, " ") + + var failure response + if len(args) > 1 { + failure.status = args[1] + } + if len(args) > 2 { + failure.kind = args[2] + } + if len(args) > 3 { + failure.model = args[3] + } + c.failures = append(c.successes, failure) } else if strings.HasPrefix(text, "@Tags ") { args := strings.SplitN(text, " ", 2) c.tags = args[1] @@ -149,7 +181,8 @@ func VerifySwaggerDefinitions(t *testing.T, router chi.Router, swaggerComments [ assertGoCommentFirst(t, *c) assertPathParametersDefined(t, *c) assertSecurityDefined(t, *c) - assertRequestBody(t, *c) + assertAccept(t, *c) + assertProduce(t, *c) }) return nil }) @@ -189,7 +222,7 @@ func assertConsistencyBetweenRouteIDAndSummary(t *testing.T, comment SwaggerComm } func assertSuccessOrFailureDefined(t *testing.T, comment SwaggerComment) { - assert.True(t, comment.hasSuccess || comment.hasFailure, "At least one @Success or @Failure annotation must be defined") + assert.True(t, len(comment.successes) > 0 || len(comment.failures) > 0, "At least one @Success or @Failure annotation must be defined") } func assertRequiredAnnotations(t *testing.T, comment SwaggerComment) { @@ -248,7 +281,7 @@ func assertSecurityDefined(t *testing.T, comment SwaggerComment) { assert.Equal(t, "CoderSessionToken", comment.security, "@Security must be equal CoderSessionToken") } -func assertRequestBody(t *testing.T, comment SwaggerComment) { +func assertAccept(t *testing.T, comment SwaggerComment) { var hasRequestBody bool for _, c := range comment.parameters { if c.name == "request" && c.kind == "body" || @@ -271,3 +304,19 @@ func assertRequestBody(t *testing.T, comment SwaggerComment) { assert.False(t, !hasRequestBody && hasAccept, "Route with @Accept annotation requires the request body or file formData parameter") } } + +func assertProduce(t *testing.T, comment SwaggerComment) { + var hasResponseModel bool + for _, r := range comment.successes { + if r.model != "" { + hasResponseModel = true + break + } + } + + if hasResponseModel { + assert.True(t, comment.produce != "", "Route must have @Produce annotation as it responds with a model structure") + } else { + assert.True(t, comment.produce == "", "Response model is undefined, so we can't predict the content type", comment) + } +} diff --git a/coderd/csp.go b/coderd/csp.go index ae3a0cbd99f93..346cd906224e8 100644 --- a/coderd/csp.go +++ b/coderd/csp.go @@ -20,7 +20,6 @@ type cspViolation struct { // @ID report-csp-violations // @Security CoderSessionToken // @Accept json -// @Produce text/plain // @Tags General // @Param request body cspViolation true "Violation report" // @Success 200 diff --git a/coderd/userauth.go b/coderd/userauth.go index 48f7a452eb19d..e2f79641bf352 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -61,7 +61,6 @@ func (api *API) userAuthMethods(rw http.ResponseWriter, r *http.Request) { // @Summary OAuth 2.0 GitHub Callback // @ID oauth-20-github-callback // @Security CoderSessionToken -// @Produce json // @Tags Users // @Success 307 // @Router /users/oauth2/github/callback [get] @@ -220,7 +219,6 @@ type OIDCConfig struct { // @Summary OpenID Connect Callback // @ID openid-connect-callback // @Security CoderSessionToken -// @Produce json // @Tags Users // @Success 307 // @Router /users/oidc/callback [get] diff --git a/coderd/users.go b/coderd/users.go index 8aec601dad8c4..ad2749163f49f 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -619,7 +619,6 @@ func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseW // @ID update-user-password // @Security CoderSessionToken // @Accept json -// @Produce json // @Tags Users // @Param user path string true "User ID, name, or me" // @Param request body codersdk.UpdateUserPasswordRequest true "Update password request" diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index e53b84bc699b0..e05c3d19a2000 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -492,7 +492,6 @@ func (api *API) workspaceAgentConnection(rw http.ResponseWriter, r *http.Request // @Description incoming connections and publishes node updates. // @ID coordinate-workspace-agent-via-tailnet // @Security CoderSessionToken -// @Produce json // @Tags Agents // @Success 101 // @Router /workspaceagents/me/coordinate [get] diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 6c9792c5a794a..798a145071613 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -566,7 +566,6 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req // @ID update-workspace-metadata-by-id // @Security CoderSessionToken // @Accept json -// @Produce json // @Tags Workspaces // @Param workspace path string true "Workspace ID" format(uuid) // @Param request body codersdk.UpdateWorkspaceRequest true "Metadata update request" @@ -656,7 +655,6 @@ func (api *API) patchWorkspace(rw http.ResponseWriter, r *http.Request) { // @ID update-workspace-autostart-schedule-by-id // @Security CoderSessionToken // @Accept json -// @Produce json // @Tags Workspaces // @Param workspace path string true "Workspace ID" format(uuid) // @Param request body codersdk.UpdateWorkspaceAutostartRequest true "Schedule update request" @@ -719,7 +717,6 @@ func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) { // @ID update-workspace-ttl-by-id // @Security CoderSessionToken // @Accept json -// @Produce json // @Tags Workspaces // @Param workspace path string true "Workspace ID" format(uuid) // @Param request body codersdk.UpdateWorkspaceTTLRequest true "Workspace TTL update request" diff --git a/enterprise/coderd/provisionerdaemons.go b/enterprise/coderd/provisionerdaemons.go index d49a96ab139d6..cbf15787a3e1b 100644 --- a/enterprise/coderd/provisionerdaemons.go +++ b/enterprise/coderd/provisionerdaemons.go @@ -94,7 +94,6 @@ func (api *API) provisionerDaemons(rw http.ResponseWriter, r *http.Request) { // @Summary Serve provisioner daemon // @ID serve-provisioner-daemon // @Security CoderSessionToken -// @Produce json // @Tags Enterprise // @Param organization path string true "Organization ID" format(uuid) // @Success 101 From 182419ace254ccdecd504577614cfb92c2548a9f Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 16:17:28 +0100 Subject: [PATCH 58/61] Fix: exceptions --- coderd/coderdtest/swaggerparser.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 319dbb54e0c76..a47f83b0335bb 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -317,6 +317,12 @@ func assertProduce(t *testing.T, comment SwaggerComment) { if hasResponseModel { assert.True(t, comment.produce != "", "Route must have @Produce annotation as it responds with a model structure") } else { + if (comment.router == "/workspaceagents/me/app-health" && comment.method == "post") || + (comment.router == "/workspaceagents/me/version" && comment.method == "post") || + (comment.router == "/licenses/{id}" && comment.method == "delete") { + return // Exception: HTTP 200 is returned without response entity + } + assert.True(t, comment.produce == "", "Response model is undefined, so we can't predict the content type", comment) } } From cb780896d2f6515e6aefdd662696f2a3150b7e54 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 12 Jan 2023 17:55:07 +0100 Subject: [PATCH 59/61] Fix field format --- coderd/apidoc/docs.go | 33 ++++++++++++++------- coderd/apidoc/swagger.json | 33 ++++++++++++++------- coderd/coderdtest/swagger_test.go | 47 ++++++++++++++++++++++++++++++ coderd/coderdtest/swaggerparser.go | 8 ++--- codersdk/audit.go | 14 ++++----- codersdk/organizations.go | 4 +-- codersdk/pagination.go | 2 +- codersdk/templates.go | 4 +-- codersdk/workspaces.go | 4 +-- docs/api/audit.md | 14 ++++----- docs/api/builds.md | 2 +- docs/api/schemas.md | 34 ++++++++++----------- docs/api/templates.md | 4 +-- docs/api/workspaces.md | 2 +- 14 files changed, 137 insertions(+), 68 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index b0942f16e8507..c003624f564e0 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -5076,7 +5076,8 @@ const docTemplate = `{ "$ref": "#/definitions/codersdk.AuditDiff" }, "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "ip": { "type": "string" @@ -5085,16 +5086,19 @@ const docTemplate = `{ "type": "boolean" }, "organization_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "request_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "resource_icon": { "type": "string" }, "resource_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "resource_link": { "type": "string" @@ -5110,7 +5114,8 @@ const docTemplate = `{ "type": "integer" }, "time": { - "type": "string" + "type": "string", + "format": "date-time" }, "user": { "$ref": "#/definitions/codersdk.User" @@ -5397,7 +5402,8 @@ const docTemplate = `{ }, "template_version_id": { "description": "VersionID is an in-progress or completed job to use as an initial version\nof the template.\n\nThis is required on creation to enable a user-flow of validating a\ntemplate works. There is no reason the data-model cannot support empty\ntemplates, but it doesn't make sense for users.", - "type": "string" + "type": "string", + "format": "uuid" } } }, @@ -5433,7 +5439,8 @@ const docTemplate = `{ ] }, "resource_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "resource_type": { "enum": [ @@ -5454,7 +5461,8 @@ const docTemplate = `{ ] }, "time": { - "type": "string" + "type": "string", + "format": "date-time" } } }, @@ -5529,7 +5537,8 @@ const docTemplate = `{ } }, "template_version_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "transition": { "enum": [ @@ -5553,7 +5562,8 @@ const docTemplate = `{ "type": "integer" }, "date": { - "type": "string" + "type": "string", + "format": "date-time" } } }, @@ -6769,7 +6779,8 @@ const docTemplate = `{ ], "properties": { "deadline": { - "type": "string" + "type": "string", + "format": "date-time" } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 4c9ee891d08c7..5f2e3f88c444b 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -4486,7 +4486,8 @@ "$ref": "#/definitions/codersdk.AuditDiff" }, "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "ip": { "type": "string" @@ -4495,16 +4496,19 @@ "type": "boolean" }, "organization_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "request_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "resource_icon": { "type": "string" }, "resource_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "resource_link": { "type": "string" @@ -4520,7 +4524,8 @@ "type": "integer" }, "time": { - "type": "string" + "type": "string", + "format": "date-time" }, "user": { "$ref": "#/definitions/codersdk.User" @@ -4779,7 +4784,8 @@ }, "template_version_id": { "description": "VersionID is an in-progress or completed job to use as an initial version\nof the template.\n\nThis is required on creation to enable a user-flow of validating a\ntemplate works. There is no reason the data-model cannot support empty\ntemplates, but it doesn't make sense for users.", - "type": "string" + "type": "string", + "format": "uuid" } } }, @@ -4809,7 +4815,8 @@ ] }, "resource_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "resource_type": { "enum": [ @@ -4830,7 +4837,8 @@ ] }, "time": { - "type": "string" + "type": "string", + "format": "date-time" } } }, @@ -4895,7 +4903,8 @@ } }, "template_version_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "transition": { "enum": ["create", "start", "stop", "delete"], @@ -4914,7 +4923,8 @@ "type": "integer" }, "date": { - "type": "string" + "type": "string", + "format": "date-time" } } }, @@ -6056,7 +6066,8 @@ "required": ["deadline"], "properties": { "deadline": { - "type": "string" + "type": "string", + "format": "date-time" } } }, diff --git a/coderd/coderdtest/swagger_test.go b/coderd/coderdtest/swagger_test.go index b6154c5a73218..ef8dd88f79c99 100644 --- a/coderd/coderdtest/swagger_test.go +++ b/coderd/coderdtest/swagger_test.go @@ -1,8 +1,13 @@ package coderdtest_test import ( + "go/ast" + "go/parser" + "go/token" + "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/coder/coder/coderd/coderdtest" @@ -17,3 +22,45 @@ func TestEndpointsDocumented(t *testing.T) { _, _, api := coderdtest.NewWithAPI(t, nil) coderdtest.VerifySwaggerDefinitions(t, api.APIHandler, swaggerComments) } + +func TestSDKFieldsFormatted(t *testing.T) { + t.Parallel() + + fileSet := token.NewFileSet() + nodes, err := parser.ParseDir(fileSet, "../../codersdk", nil, parser.ParseComments) + require.NoError(t, err, "parser.ParseDir failed") + + for _, node := range nodes { + ast.Inspect(node, func(n ast.Node) bool { + typeSpec, ok := n.(*ast.TypeSpec) + if !ok { + return true + } + structureName := typeSpec.Name + + structType, ok := typeSpec.Type.(*ast.StructType) + if !ok { + return true // not a structure + } + + for _, field := range structType.Fields.List { + selectorExpr, ok := field.Type.(*ast.SelectorExpr) + if !ok { + continue // rather a basic, or primitive + } + + if field.Tag == nil || !strings.Contains(field.Tag.Value, `json:"`) { + continue // not a JSON property + } + + switch selectorExpr.Sel.Name { + case "UUID": + assert.Contains(t, field.Tag.Value, `format:"uuid"`, `Swagger formatting requires to annotate the field with - format:"uuid". Location: %s/%s`, structureName, field.Names) + case "Time": + assert.Contains(t, field.Tag.Value, `format:"date-time"`, `Swagger formatting requires to annotate the field with - format:"date-time". Location: %s/%s`, structureName, field.Names) + } + } + return true + }) + } +} diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index a47f83b0335bb..59bb36308d246 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -50,13 +50,13 @@ func ParseSwaggerComments(dirs ...string) ([]SwaggerComment, error) { var swaggerComments []SwaggerComment for _, dir := range dirs { - commentNodes, err := parser.ParseDir(fileSet, dir, nil, parser.ParseComments) + nodes, err := parser.ParseDir(fileSet, dir, nil, parser.ParseComments) if err != nil { - return nil, xerrors.Errorf(`parser.ParseDir failed "%s": %w`, dir, err) + return nil, xerrors.Errorf(`parser.ParseDir failed for "%s": %w`, dir, err) } - for _, commentNode := range commentNodes { - ast.Inspect(commentNode, func(n ast.Node) bool { + for _, node := range nodes { + ast.Inspect(node, func(n ast.Node) bool { commentGroup, ok := n.(*ast.CommentGroup) if !ok { return true diff --git a/codersdk/audit.go b/codersdk/audit.go index da8f88df04ade..4ae98466c3798 100644 --- a/codersdk/audit.go +++ b/codersdk/audit.go @@ -88,14 +88,14 @@ type AuditDiffField struct { } type AuditLog struct { - ID uuid.UUID `json:"id"` - RequestID uuid.UUID `json:"request_id"` - Time time.Time `json:"time"` - OrganizationID uuid.UUID `json:"organization_id"` + ID uuid.UUID `json:"id" format:"uuid"` + RequestID uuid.UUID `json:"request_id" format:"uuid"` + Time time.Time `json:"time" format:"date-time"` + OrganizationID uuid.UUID `json:"organization_id" format:"uuid"` IP netip.Addr `json:"ip"` UserAgent string `json:"user_agent"` ResourceType ResourceType `json:"resource_type"` - ResourceID uuid.UUID `json:"resource_id"` + ResourceID uuid.UUID `json:"resource_id" format:"uuid"` // ResourceTarget is the name of the resource. ResourceTarget string `json:"resource_target"` ResourceIcon string `json:"resource_icon"` @@ -123,8 +123,8 @@ type AuditLogResponse struct { type CreateTestAuditLogRequest struct { Action AuditAction `json:"action,omitempty" enums:"create,write,delete,start,stop"` ResourceType ResourceType `json:"resource_type,omitempty" enums:"organization,template,template_version,user,workspace,workspace_build,git_ssh_key,api_key,group"` - ResourceID uuid.UUID `json:"resource_id,omitempty"` - Time time.Time `json:"time,omitempty"` + ResourceID uuid.UUID `json:"resource_id,omitempty" format:"uuid"` + Time time.Time `json:"time,omitempty" format:"date-time"` } // AuditLogs retrieves audit logs from the given page. diff --git a/codersdk/organizations.go b/codersdk/organizations.go index 27a30f8a14583..1c21f5cc399d1 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -67,7 +67,7 @@ type CreateTemplateRequest struct { // This is required on creation to enable a user-flow of validating a // template works. There is no reason the data-model cannot support empty // templates, but it doesn't make sense for users. - VersionID uuid.UUID `json:"template_version_id" validate:"required"` + VersionID uuid.UUID `json:"template_version_id" validate:"required" format:"uuid"` ParameterValues []CreateParameterRequest `json:"parameter_values,omitempty"` // DefaultTTLMillis allows optionally specifying the default TTL @@ -81,7 +81,7 @@ type CreateTemplateRequest struct { // CreateWorkspaceRequest provides options for creating a new workspace. type CreateWorkspaceRequest struct { - TemplateID uuid.UUID `json:"template_id" validate:"required"` + TemplateID uuid.UUID `json:"template_id" validate:"required" format:"uuid"` Name string `json:"name" validate:"workspace_name,required"` AutostartSchedule *string `json:"autostart_schedule"` TTLMillis *int64 `json:"ttl_ms,omitempty"` diff --git a/codersdk/pagination.go b/codersdk/pagination.go index 7e6cf8d1dd41f..2201277aecaa8 100644 --- a/codersdk/pagination.go +++ b/codersdk/pagination.go @@ -14,7 +14,7 @@ type Pagination struct { // Offset for better performance. To use it as an alternative, // set AfterID to the last UUID returned by the previous // request. - AfterID uuid.UUID `json:"after_id,omitempty"` + AfterID uuid.UUID `json:"after_id,omitempty" format:"uuid"` // Limit sets the maximum number of users to be returned // in a single page. If the limit is <= 0, there is no limit // and all users are returned. diff --git a/codersdk/templates.go b/codersdk/templates.go index bf00bd7e3de9a..44471a234e86f 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -176,7 +176,7 @@ func (c *Client) UpdateActiveTemplateVersion(ctx context.Context, template uuid. // TemplateVersionsByTemplateRequest defines the request parameters for // TemplateVersionsByTemplate. type TemplateVersionsByTemplateRequest struct { - TemplateID uuid.UUID `json:"template_id" validate:"required"` + TemplateID uuid.UUID `json:"template_id" validate:"required" format:"uuid"` Pagination } @@ -210,7 +210,7 @@ func (c *Client) TemplateVersionByName(ctx context.Context, template uuid.UUID, } type DAUEntry struct { - Date time.Time `json:"date"` + Date time.Time `json:"date" format:"date-time"` Amount int `json:"amount"` } diff --git a/codersdk/workspaces.go b/codersdk/workspaces.go index 75901bc4923ec..3c1f1a8bda84f 100644 --- a/codersdk/workspaces.go +++ b/codersdk/workspaces.go @@ -47,7 +47,7 @@ type WorkspacesResponse struct { // CreateWorkspaceBuildRequest provides options to update the latest workspace build. type CreateWorkspaceBuildRequest struct { - TemplateVersionID uuid.UUID `json:"template_version_id,omitempty"` + TemplateVersionID uuid.UUID `json:"template_version_id,omitempty" format:"uuid"` Transition WorkspaceTransition `json:"transition" validate:"oneof=create start stop delete,required"` DryRun bool `json:"dry_run,omitempty"` ProvisionerState []byte `json:"state,omitempty"` @@ -245,7 +245,7 @@ func (c *Client) UpdateWorkspaceTTL(ctx context.Context, id uuid.UUID, req Updat // PutExtendWorkspaceRequest is a request to extend the deadline of // the active workspace build. type PutExtendWorkspaceRequest struct { - Deadline time.Time `json:"deadline" validate:"required"` + Deadline time.Time `json:"deadline" validate:"required" format:"date-time"` } // PutExtendWorkspace updates the deadline for resources of the latest workspace build. diff --git a/docs/api/audit.md b/docs/api/audit.md index 743807ee328a3..22ddec0b690c3 100644 --- a/docs/api/audit.md +++ b/docs/api/audit.md @@ -47,18 +47,18 @@ curl -X GET http://coder-server:8080/api/v2/audit?q=string \ "secret": true } }, - "id": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "ip": "string", "is_deleted": true, - "organization_id": "string", - "request_id": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "request_id": "266ea41d-adf5-480b-af50-15b940c2b846", "resource_icon": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "resource_link": "string", "resource_target": "string", "resource_type": "organization", "status_code": 0, - "time": "string", + "time": "2019-08-24T14:15:22Z", "user": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", @@ -108,9 +108,9 @@ curl -X POST http://coder-server:8080/api/v2/audit/testgenerate \ ```json { "action": "create", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "resource_type": "organization", - "time": "string" + "time": "2019-08-24T14:15:22Z" } ``` diff --git a/docs/api/builds.md b/docs/api/builds.md index 9cb3c9abcd6a4..77e7f666b814d 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -1043,7 +1043,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ } ], "state": [0], - "template_version_id": "string", + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", "transition": "create" } ``` diff --git a/docs/api/schemas.md b/docs/api/schemas.md index a9459f2be5401..abc4d9d012e7a 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -323,18 +323,18 @@ "secret": true } }, - "id": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "ip": "string", "is_deleted": true, - "organization_id": "string", - "request_id": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "request_id": "266ea41d-adf5-480b-af50-15b940c2b846", "resource_icon": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "resource_link": "string", "resource_target": "string", "resource_type": "organization", "status_code": 0, - "time": "string", + "time": "2019-08-24T14:15:22Z", "user": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", @@ -399,18 +399,18 @@ "secret": true } }, - "id": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "ip": "string", "is_deleted": true, - "organization_id": "string", - "request_id": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "request_id": "266ea41d-adf5-480b-af50-15b940c2b846", "resource_icon": "string", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "resource_link": "string", "resource_target": "string", "resource_type": "organization", "status_code": 0, - "time": "string", + "time": "2019-08-24T14:15:22Z", "user": { "avatar_url": "http://example.com", "created_at": "2019-08-24T14:15:22Z", @@ -731,7 +731,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a "source_value": "string" } ], - "template_version_id": "string" + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1" } ``` @@ -778,9 +778,9 @@ CreateParameterRequest is a structure used to create a new parameter value for a ```json { "action": "create", - "resource_id": "string", + "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "resource_type": "organization", - "time": "string" + "time": "2019-08-24T14:15:22Z" } ``` @@ -871,7 +871,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a } ], "state": [0], - "template_version_id": "string", + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", "transition": "create" } ``` @@ -901,7 +901,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a ```json { "amount": 0, - "date": "string" + "date": "2019-08-24T14:15:22Z" } ``` @@ -3431,7 +3431,7 @@ Parameter represents a set value for the scope. ```json { - "deadline": "string" + "deadline": "2019-08-24T14:15:22Z" } ``` @@ -3875,7 +3875,7 @@ Parameter represents a set value for the scope. "entries": [ { "amount": 0, - "date": "string" + "date": "2019-08-24T14:15:22Z" } ] } diff --git a/docs/api/templates.md b/docs/api/templates.md index 3d0ae02869189..268bd91d56961 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -204,7 +204,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "source_value": "string" } ], - "template_version_id": "string" + "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1" } ``` @@ -820,7 +820,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/daus \ "entries": [ { "amount": 0, - "date": "string" + "date": "2019-08-24T14:15:22Z" } ] } diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 7e12be69d76bf..a977d43936c95 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -755,7 +755,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/extend \ ```json { - "deadline": "string" + "deadline": "2019-08-24T14:15:22Z" } ``` From fa2d03074ed3d09577ac2174f2cf48739909affc Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 13 Jan 2023 11:07:04 +0100 Subject: [PATCH 60/61] Address PR comments --- coderd/coderdtest/swaggerparser.go | 89 +++++++++++++++--------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 59bb36308d246..20825c01ed59e 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -89,69 +89,70 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment { failures: []response{}, } for _, line := range commentGroup.List { - splitN := strings.SplitN(strings.TrimSpace(line.Text), " ", 2) + splitN := strings.SplitN(strings.TrimSpace(line.Text), " ", 3) // @Router if len(splitN) < 2 { continue // comment prefix without any content } - text := splitN[1] // Skip the comment prefix (double-slash) - - if strings.HasPrefix(text, "@Router ") { - args := strings.SplitN(text, " ", 3) - c.router = args[1] - c.method = args[2][1 : len(args[2])-1] - } else if strings.HasPrefix(text, "@Summary ") { - args := strings.SplitN(text, " ", 2) - c.summary = args[1] - } else if strings.HasPrefix(text, "@ID ") { - args := strings.SplitN(text, " ", 2) - c.id = args[1] - } else if strings.HasPrefix(text, "@Success ") { - args := strings.Split(text, " ") + + if !strings.HasPrefix(splitN[1], "@") { + continue // not a swagger annotation + } + + annotationName := splitN[1] + annotationArgs := splitN[2] + + switch annotationName { + case "@Router": + args := strings.SplitN(annotationArgs, " ", 2) + c.router = args[0] + c.method = args[1][1 : len(args[1])-1] + case "@Success": + args := strings.Split(annotationArgs, " ") var success response + if len(args) > 0 { + success.status = args[0] + } if len(args) > 1 { - success.status = args[1] + success.kind = args[1] } if len(args) > 2 { - success.kind = args[2] - } - if len(args) > 3 { - success.model = args[3] + success.model = args[2] } c.successes = append(c.successes, success) - } else if strings.HasPrefix(text, "@Failure ") { - args := strings.Split(text, " ") + case "@Failure": + args := strings.Split(annotationArgs, " ") var failure response + if len(args) > 0 { + failure.status = args[0] + } if len(args) > 1 { - failure.status = args[1] + failure.kind = args[1] } if len(args) > 2 { - failure.kind = args[2] - } - if len(args) > 3 { - failure.model = args[3] + failure.model = args[2] } - c.failures = append(c.successes, failure) - } else if strings.HasPrefix(text, "@Tags ") { - args := strings.SplitN(text, " ", 2) - c.tags = args[1] - } else if strings.HasPrefix(text, "@Security ") { - args := strings.SplitN(text, " ", 2) - c.security = args[1] - } else if strings.HasPrefix(text, "@Param ") { - args := strings.SplitN(text, " ", 4) + c.failures = append(c.failures, failure) + case "@Param": + args := strings.SplitN(annotationArgs, " ", 3) p := parameter{ - name: args[1], - kind: args[2], + name: args[0], + kind: args[1], } c.parameters = append(c.parameters, p) - } else if strings.HasPrefix(text, "@Accept ") { - args := strings.SplitN(text, " ", 2) - c.accept = args[1] - } else if strings.HasPrefix(text, "@Produce ") { - args := strings.SplitN(text, " ", 2) - c.produce = args[1] + case "@Summary": + c.summary = annotationArgs + case "@ID": + c.id = annotationArgs + case "@Tags": + c.tags = annotationArgs + case "@Security": + c.security = annotationArgs + case "@Accept": + c.accept = annotationArgs + case "@Produce": + c.produce = annotationArgs } } return c From ecb2d5a1b6d6a607a2859d0a113ad7296b936044 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 13 Jan 2023 11:12:59 +0100 Subject: [PATCH 61/61] Refactor --- coderd/coderdtest/swaggerparser.go | 35 ++++++++++-------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 20825c01ed59e..ccdb082d4a328 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -89,7 +89,8 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment { failures: []response{}, } for _, line := range commentGroup.List { - splitN := strings.SplitN(strings.TrimSpace(line.Text), " ", 3) // @Router + // @ [args...] + splitN := strings.SplitN(strings.TrimSpace(line.Text), " ", 3) if len(splitN) < 2 { continue // comment prefix without any content } @@ -100,42 +101,30 @@ func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment { annotationName := splitN[1] annotationArgs := splitN[2] + args := strings.Split(splitN[2], " ") switch annotationName { case "@Router": - args := strings.SplitN(annotationArgs, " ", 2) c.router = args[0] c.method = args[1][1 : len(args[1])-1] - case "@Success": - args := strings.Split(annotationArgs, " ") - - var success response + case "@Success", "@Failure": + var r response if len(args) > 0 { - success.status = args[0] + r.status = args[0] } if len(args) > 1 { - success.kind = args[1] + r.kind = args[1] } if len(args) > 2 { - success.model = args[2] + r.model = args[2] } - c.successes = append(c.successes, success) - case "@Failure": - args := strings.Split(annotationArgs, " ") - var failure response - if len(args) > 0 { - failure.status = args[0] - } - if len(args) > 1 { - failure.kind = args[1] - } - if len(args) > 2 { - failure.model = args[2] + if annotationName == "@Success" { + c.successes = append(c.successes, r) + } else if annotationName == "@Failure" { + c.failures = append(c.failures, r) } - c.failures = append(c.failures, failure) case "@Param": - args := strings.SplitN(annotationArgs, " ", 3) p := parameter{ name: args[0], kind: args[1],