feat: Validate swagger definitions (#5694)

* docs: audit, deploymentconfig, files, parameters

* Swagger comments in workspacebuilds.go

* structs in workspacebuilds.go

* workspaceagents: instance identity

* workspaceagents.go in progress

* workspaceagents.go in progress

* Agents

* workspacebuilds.go

* /workspaces

* templates.go, templateversions.go

* templateversion.go in progress

* cancel

* templateversions

* wip

* Merge

* x-apidocgen

* NullTime hack not needed anymore

* Fix: x-apidocgen

* Members

* Fixes

* Fix

* WIP

* WIP

* Users

* Logout

* User profile

* Status suspend activate

* User roles

* User tokens

* Keys

* SSH key

* All

* Typo

* Fix

* Entitlements

* Groups

* SCIM

* Fix

* Fix

* Clean templates

* Sort API pages

* Fix: HashedSecret

* WIP

* WIP

* WIP

* Fix: cover workspaceagents

* Assert: consistent ID and summary

* Assert: success or failure defined

* Fix: parallel

* Refactor

* Support enterprise

* Go comment goes to top

* Security

* assertPathParametersDefined

* assertUniqueRoutes

* assertRequestBody

* More fixes

* Fix: exceptions

* Fix field format

* Address PR comments

* Refactor
This commit is contained in:
Marcin Tojek
2023-01-13 12:27:21 +01:00
committed by GitHub
parent dcab87358e
commit deebfcbd53
39 changed files with 2442 additions and 705 deletions
+622 -264
View File
File diff suppressed because it is too large Load Diff
+542 -222
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -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"
@@ -209,9 +210,8 @@ 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
// @Param user path string true "User ID, name, or me"
// @Param keyid path string true "Key ID" format(uuid)
+1 -1
View File
@@ -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
+66
View File
@@ -0,0 +1,66 @@
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"
)
func TestEndpointsDocumented(t *testing.T) {
t.Parallel()
swaggerComments, err := coderdtest.ParseSwaggerComments("..")
require.NoError(t, err, "can't parse swagger comments")
_, _, 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
})
}
}
+318
View File
@@ -0,0 +1,318 @@
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
security string
tags string
accept string
produce string
method string
router string
successes []response
failures []response
parameters []parameter
raw []*ast.Comment
}
type parameter struct {
name string
kind string
}
type response struct {
status string
kind string // {object} or {array}
model string
}
func ParseSwaggerComments(dirs ...string) ([]SwaggerComment, error) {
fileSet := token.NewFileSet()
var swaggerComments []SwaggerComment
for _, dir := range dirs {
nodes, err := parser.ParseDir(fileSet, dir, nil, parser.ParseComments)
if err != nil {
return nil, xerrors.Errorf(`parser.ParseDir failed for "%s": %w`, dir, err)
}
for _, node := range nodes {
ast.Inspect(node, 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 {
c := SwaggerComment{
raw: commentGroup.List,
parameters: []parameter{},
successes: []response{},
failures: []response{},
}
for _, line := range commentGroup.List {
// @<annotationName> [args...]
splitN := strings.SplitN(strings.TrimSpace(line.Text), " ", 3)
if len(splitN) < 2 {
continue // comment prefix without any content
}
if !strings.HasPrefix(splitN[1], "@") {
continue // not a swagger annotation
}
annotationName := splitN[1]
annotationArgs := splitN[2]
args := strings.Split(splitN[2], " ")
switch annotationName {
case "@Router":
c.router = args[0]
c.method = args[1][1 : len(args[1])-1]
case "@Success", "@Failure":
var r response
if len(args) > 0 {
r.status = args[0]
}
if len(args) > 1 {
r.kind = args[1]
}
if len(args) > 2 {
r.model = args[2]
}
if annotationName == "@Success" {
c.successes = append(c.successes, r)
} else if annotationName == "@Failure" {
c.failures = append(c.failures, r)
}
case "@Param":
p := parameter{
name: args[0],
kind: args[1],
}
c.parameters = append(c.parameters, p)
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
}
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, "/") {
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)
assertGoCommentFirst(t, *c)
assertPathParametersDefined(t, *c)
assertSecurityDefined(t, *c)
assertAccept(t, *c)
assertProduce(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 {
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, 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) {
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")
}
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
}
}
}
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) {
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")
}
func assertAccept(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")
}
}
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 {
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)
}
}
-1
View File
@@ -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
+1 -1
View File
@@ -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
-1
View File
@@ -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
+1
View File
@@ -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"
+14 -12
View File
@@ -23,15 +23,16 @@ import (
"github.com/coder/coder/examples"
)
// Returns a single template.
//
// @Summary Get template metadata by ID
// @ID get-template-metadata-by-id
// @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]
// Returns a single template.
// @Router /templates/{template} [get]
func (api *API) template(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
template := httpmw.TemplateParam(r)
@@ -75,9 +76,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()
@@ -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()
@@ -461,13 +463,13 @@ 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
// @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 +595,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)
+29 -29
View File
@@ -353,13 +353,12 @@ 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)
// @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 +375,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 +393,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 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/{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 {
@@ -414,9 +413,10 @@ 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]
// @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)
@@ -536,16 +536,16 @@ 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
// @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)
@@ -828,15 +828,15 @@ 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
// @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()
@@ -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()
+2 -4
View File
@@ -59,9 +59,8 @@ 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
// @Success 307
// @Router /users/oauth2/github/callback [get]
@@ -218,9 +217,8 @@ type OIDCConfig struct {
}
// @Summary OpenID Connect Callback
// @ID oidc-callback
// @ID openid-connect-callback
// @Security CoderSessionToken
// @Produce json
// @Tags Users
// @Success 307
// @Router /users/oidc/callback [get]
+4 -2
View File
@@ -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,7 +618,7 @@ func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseW
// @Summary Update user password
// @ID update-user-password
// @Security CoderSessionToken
// @Produce json
// @Accept json
// @Tags Users
// @Param user path string true "User ID, name, or me"
// @Param request body codersdk.UpdateUserPasswordRequest true "Update password request"
@@ -908,7 +909,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
@@ -990,6 +991,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"
+49 -9
View File
@@ -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)
@@ -66,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
@@ -147,9 +154,10 @@ 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
// @Accept json
// @Produce json
// @Tags Agents
// @Param request body codersdk.PostWorkspaceAgentVersionRequest true "Version request"
// @Success 200
@@ -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)
@@ -458,9 +490,8 @@ 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
// @Success 101
// @Router /workspaceagents/me/coordinate [get]
@@ -622,6 +653,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()
@@ -784,8 +823,9 @@ 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
// @Accept json
// @Produce application/json
// @Tags Agents
// @Param request body codersdk.AgentStats true "Stats request"
@@ -860,9 +900,10 @@ 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
// @Accept json
// @Produce application/json
// @Tags Agents
// @Param request body codersdk.PostWorkspaceAppHealthsRequest true "Application health request"
@@ -989,7 +1030,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)
+8 -8
View File
@@ -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
@@ -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 == "" {
+5 -5
View File
@@ -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)
@@ -290,13 +290,13 @@ 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 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)
+11 -8
View File
@@ -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"
@@ -45,18 +46,19 @@ 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
// @Accept json
// @Produce json
// @Tags Agents
// @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,18 +76,19 @@ 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
// @Accept json
// @Produce json
// @Tags Agents
// @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
+9 -12
View File
@@ -48,10 +48,10 @@ 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/{id} [get]
// @Router /workspaces/{workspace} [get]
func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
workspace := httpmw.WorkspaceParam(r)
@@ -101,8 +101,11 @@ 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 get-workspaces
// @ID list-workspaces
// @Security CoderSessionToken
// @Produce json
// @Tags Workspaces
@@ -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()
@@ -558,7 +558,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"
@@ -648,7 +647,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"
@@ -711,7 +709,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"
@@ -875,7 +872,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
+7 -7
View File
@@ -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.
+2 -2
View File
@@ -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"`
+1 -1
View File
@@ -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.
+2 -2
View File
@@ -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"`
}
+2 -2
View File
@@ -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.
+269 -1
View File
@@ -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
@@ -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).
+7 -7
View File
@@ -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"
}
```
+16 -16
View File
@@ -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
@@ -1043,17 +1043,17 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{id}/builds \
}
],
"state": [0],
"template_version_id": "string",
"template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1",
"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 |
| 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
+212 -14
View File
@@ -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
@@ -609,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
+133 -17
View File
@@ -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"
}
```
@@ -2561,6 +2561,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
@@ -3463,7 +3515,7 @@ Parameter represents a set value for the scope.
```json
{
"deadline": "string"
"deadline": "2019-08-24T14:15:22Z"
}
```
@@ -3907,7 +3959,7 @@ Parameter represents a set value for the scope.
"entries": [
{
"amount": 0,
"date": "string"
"date": "2019-08-24T14:15:22Z"
}
]
}
@@ -4617,6 +4669,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
+46 -45
View File
@@ -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"
}
```
@@ -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
@@ -820,7 +820,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{id}/daus \
"entries": [
{
"amount": 0,
"date": "string"
"date": "2019-08-24T14:15:22Z"
}
]
}
@@ -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,17 +1390,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/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
| Name | In | Type | Required | Description |
| ----------------- | ---- | ------------ | -------- | ------------------- |
| `jobID` | path | string(uuid) | true | Job ID |
| `templateversion` | path | string(uuid) | true | Template version ID |
### Example responses
@@ -1434,19 +1435,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 |
| `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 |
@@ -1508,19 +1509,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
+4 -4
View File
@@ -511,18 +511,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/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
| 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
@@ -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"
}
```
+1
View File
@@ -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"
+1 -1
View File
@@ -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) {
@@ -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)
}
+31 -2
View File
@@ -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()
+2 -1
View File
@@ -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"
@@ -178,7 +179,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) {
-1
View File
@@ -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
+1 -1
View File
@@ -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) {
+1
View File
@@ -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)