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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 2442 additions and 705 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,7 @@ import (
// @Summary Create token API key // @Summary Create token API key
// @ID create-token-api-key // @ID create-token-api-key
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Users // @Tags Users
// @Param user path string true "User ID, name, or me" // @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 // @Summary Delete API key
// @ID delete-user-tokens // @ID delete-api-key
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json
// @Tags Users // @Tags Users
// @Param user path string true "User ID, name, or me" // @Param user path string true "User ID, name, or me"
// @Param keyid path string true "Key ID" format(uuid) // @Param keyid path string true "Key ID" format(uuid)

View File

@ -89,7 +89,7 @@ func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) {
} }
// @Summary Generate fake audit log // @Summary Generate fake audit log
// @ID generate-fake-audit-logs // @ID generate-fake-audit-log
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json // @Accept json
// @Tags Audit // @Tags Audit

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
})
}
}

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)
}
}

View File

@ -20,7 +20,6 @@ type cspViolation struct {
// @ID report-csp-violations // @ID report-csp-violations
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json // @Accept json
// @Produce text/plain
// @Tags General // @Tags General
// @Param request body cspViolation true "Violation report" // @Param request body cspViolation true "Violation report"
// @Success 200 // @Success 200

View File

@ -25,7 +25,7 @@ const (
// @Summary Upload file // @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`. // @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 // @Security CoderSessionToken
// @Produce json // @Produce json
// @Accept application/x-tar // @Accept application/x-tar

View File

@ -119,7 +119,6 @@ func (api *API) gitSSHKey(rw http.ResponseWriter, r *http.Request) {
// @Summary Get workspace agent Git SSH key // @Summary Get workspace agent Git SSH key
// @ID get-workspace-agent-git-ssh-key // @ID get-workspace-agent-git-ssh-key
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Agents // @Tags Agents
// @Success 200 {object} codersdk.AgentGitSSHKey // @Success 200 {object} codersdk.AgentGitSSHKey

View File

@ -40,6 +40,7 @@ func (api *API) organization(rw http.ResponseWriter, r *http.Request) {
// @Summary Create organization // @Summary Create organization
// @ID create-organization // @ID create-organization
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Organizations // @Tags Organizations
// @Param request body codersdk.CreateOrganizationRequest true "Create organization request" // @Param request body codersdk.CreateOrganizationRequest true "Create organization request"

View File

@ -23,15 +23,16 @@ import (
"github.com/coder/coder/examples" "github.com/coder/coder/examples"
) )
// Returns a single template.
//
// @Summary Get template metadata by ID // @Summary Get template metadata by ID
// @ID get-template-metadata-by-id // @ID get-template-metadata-by-id
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Templates // @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 // @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) { func (api *API) template(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
template := httpmw.TemplateParam(r) template := httpmw.TemplateParam(r)
@ -75,9 +76,9 @@ func (api *API) template(rw http.ResponseWriter, r *http.Request) {
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Templates // @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 // @Success 200 {object} codersdk.Response
// @Router /templates/{id} [delete] // @Router /templates/{template} [delete]
func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) { func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() 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 // @Summary Create template by organization
// @ID create-template-by-organization // @ID create-template-by-organization
// @Security CoderSessionToken // @Security CoderSessionToken
@ -141,8 +145,6 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
// @Param organization path string true "Organization ID" // @Param organization path string true "Organization ID"
// @Success 200 {object} codersdk.Template // @Success 200 {object} codersdk.Template
// @Router /organizations/{organization}/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) { func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() ctx = r.Context()
@ -461,13 +463,13 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re
} }
// @Summary Update template metadata by ID // @Summary Update template metadata by ID
// @ID update-template-metadata // @ID update-template-metadata-by-id
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Templates // @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 // @Success 200 {object} codersdk.Template
// @Router /templates/{id} [patch] // @Router /templates/{template} [patch]
func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() ctx = r.Context()
@ -593,9 +595,9 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Templates // @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 // @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) { func (api *API) templateDAUs(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
template := httpmw.TemplateParam(r) template := httpmw.TemplateParam(r)

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 // @Summary Get template version dry-run by job ID
// @ID get-template-version-dry-run-by-job-id // @ID get-template-version-dry-run-by-job-id
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Templates // @Tags Templates
// @Param templateversion 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 jobID path string true "Job ID" format(uuid)
// @Success 200 {object} codersdk.ProvisionerJob // @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) { func (api *API) templateVersionDryRun(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
job, ok := api.fetchTemplateVersionDryRunJob(rw, r) job, ok := api.fetchTemplateVersionDryRunJob(rw, r)
@ -376,9 +375,9 @@ func (api *API) templateVersionDryRun(rw http.ResponseWriter, r *http.Request) {
// @Produce json // @Produce json
// @Tags Templates // @Tags Templates
// @Param templateversion 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 jobID path string true "Job ID" format(uuid)
// @Success 200 {array} codersdk.WorkspaceResource // @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) { func (api *API) templateVersionDryRunResources(rw http.ResponseWriter, r *http.Request) {
job, ok := api.fetchTemplateVersionDryRunJob(rw, r) job, ok := api.fetchTemplateVersionDryRunJob(rw, r)
if !ok { if !ok {
@ -394,12 +393,12 @@ func (api *API) templateVersionDryRunResources(rw http.ResponseWriter, r *http.R
// @Produce json // @Produce json
// @Tags Templates // @Tags Templates
// @Param templateversion 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 jobID path string true "Job ID" format(uuid)
// @Param before query int false "Before Unix timestamp" // @Param before query int false "Before Unix timestamp"
// @Param after query int false "After Unix timestamp" // @Param after query int false "After Unix timestamp"
// @Param follow query bool false "Follow log stream" // @Param follow query bool false "Follow log stream"
// @Success 200 {array} codersdk.ProvisionerJobLog // @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) { func (api *API) templateVersionDryRunLogs(rw http.ResponseWriter, r *http.Request) {
job, ok := api.fetchTemplateVersionDryRunJob(rw, r) job, ok := api.fetchTemplateVersionDryRunJob(rw, r)
if !ok { if !ok {
@ -414,9 +413,10 @@ func (api *API) templateVersionDryRunLogs(rw http.ResponseWriter, r *http.Reques
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Templates // @Tags Templates
// @Param jobID path string true "Job ID" format(uuid)
// @Param templateversion path string true "Template version ID" format(uuid) // @Param templateversion path string true "Template version ID" format(uuid)
// @Success 200 {object} codersdk.Response // @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) { func (api *API) patchTemplateVersionDryRunCancel(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
templateVersion := httpmw.TemplateVersionParam(r) 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 // @Summary List template versions by template ID
// @ID list-template-versions-by-template-ID // @ID list-template-versions-by-template-id
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Templates // @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 after_id query string false "After ID" format(uuid)
// @Param limit query int false "Page limit" // @Param limit query int false "Page limit"
// @Param offset query int false "Page offset" // @Param offset query int false "Page offset"
// @Success 200 {array} codersdk.TemplateVersion // @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) { func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
template := httpmw.TemplateParam(r) template := httpmw.TemplateParam(r)
@ -648,10 +648,10 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Templates // @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" // @Param templateversionname path string true "Template version name"
// @Success 200 {array} codersdk.TemplateVersion // @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) { func (api *API) templateVersionByName(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
template := httpmw.TemplateParam(r) template := httpmw.TemplateParam(r)
@ -828,15 +828,15 @@ func (api *API) previousTemplateVersionByOrganizationAndName(rw http.ResponseWri
} }
// @Summary Update active template version by template ID // @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 // @Security CoderSessionToken
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Tags Templates // @Tags Templates
// @Param request body codersdk.UpdateActiveTemplateVersion true "Modified template version" // @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 // @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) { func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() 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 // @Summary Create template version by organization
// @ID create-template-version-by-organization // @ID create-template-version-by-organization
// @Security CoderSessionToken // @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" // @Param request body codersdk.CreateTemplateVersionDryRunRequest true "Create template version request"
// @Success 201 {object} codersdk.TemplateVersion // @Success 201 {object} codersdk.TemplateVersion
// @Router /organizations/{organization}/templateversions [post] // @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) { func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() 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)) 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 // @Summary Get resources by template version
// @ID get-resources-by-template-version // @ID get-resources-by-template-version
// @Security CoderSessionToken // @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) // @Param templateversion path string true "Template version ID" format(uuid)
// @Success 200 {array} codersdk.WorkspaceResource // @Success 200 {array} codersdk.WorkspaceResource
// @Router /templateversions/{templateversion}/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
// 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) { func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() ctx = r.Context()
@ -1244,6 +1244,11 @@ func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request
api.provisionerJobResources(rw, r, job) 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 // @Summary Get logs by template version
// @ID get-logs-by-template-version // @ID get-logs-by-template-version
// @Security CoderSessionToken // @Security CoderSessionToken
@ -1255,11 +1260,6 @@ func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request
// @Param follow query bool false "Follow log stream" // @Param follow query bool false "Follow log stream"
// @Success 200 {array} codersdk.ProvisionerJobLog // @Success 200 {array} codersdk.ProvisionerJobLog
// @Router /templateversions/{templateversion}/logs [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,
// 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) { func (api *API) templateVersionLogs(rw http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() ctx = r.Context()

View File

@ -59,9 +59,8 @@ func (api *API) userAuthMethods(rw http.ResponseWriter, r *http.Request) {
} }
// @Summary OAuth 2.0 GitHub Callback // @Summary OAuth 2.0 GitHub Callback
// @ID oauth2-github-callback // @ID oauth-20-github-callback
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json
// @Tags Users // @Tags Users
// @Success 307 // @Success 307
// @Router /users/oauth2/github/callback [get] // @Router /users/oauth2/github/callback [get]
@ -218,9 +217,8 @@ type OIDCConfig struct {
} }
// @Summary OpenID Connect Callback // @Summary OpenID Connect Callback
// @ID oidc-callback // @ID openid-connect-callback
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json
// @Tags Users // @Tags Users
// @Success 307 // @Success 307
// @Router /users/oidc/callback [get] // @Router /users/oidc/callback [get]

View File

@ -435,6 +435,7 @@ func (api *API) userByName(rw http.ResponseWriter, r *http.Request) {
// @Summary Update user profile // @Summary Update user profile
// @ID update-user-profile // @ID update-user-profile
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Users // @Tags Users
// @Param user path string true "User ID, name, or me" // @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 // @Summary Update user password
// @ID update-user-password // @ID update-user-password
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Accept json
// @Tags Users // @Tags Users
// @Param user path string true "User ID, name, or me" // @Param user path string true "User ID, name, or me"
// @Param request body codersdk.UpdateUserPasswordRequest true "Update password request" // @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. // Returns organizations the parameterized user has access to.
// //
// @Summary Get organizations by user // @Summary Get organizations by user
// @ID get-organizations-by-users // @ID get-organizations-by-user
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Users // @Tags Users
@ -990,6 +991,7 @@ func (api *API) organizationByUserAndName(rw http.ResponseWriter, r *http.Reques
// @Summary Log in user // @Summary Log in user
// @ID log-in-user // @ID log-in-user
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Authorization // @Tags Authorization
// @Param request body codersdk.LoginWithPasswordRequest true "Login request" // @Param request body codersdk.LoginWithPasswordRequest true "Login request"

View File

@ -35,6 +35,14 @@ import (
"github.com/coder/coder/tailnet" "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) { func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
workspaceAgent := httpmw.WorkspaceAgentParam(r) workspaceAgent := httpmw.WorkspaceAgentParam(r)
@ -66,7 +74,6 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
// @Summary Get authorized workspace agent metadata // @Summary Get authorized workspace agent metadata
// @ID get-authorized-workspace-agent-metadata // @ID get-authorized-workspace-agent-metadata
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Agents // @Tags Agents
// @Success 200 {object} codersdk.WorkspaceAgentMetadata // @Success 200 {object} codersdk.WorkspaceAgentMetadata
@ -147,9 +154,10 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request)
} }
// @Summary Submit workspace agent version // @Summary Submit workspace agent version
// @ID submit-workspace-workspace-agent-version // @ID submit-workspace-agent-version
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce application/json // @Accept json
// @Produce json
// @Tags Agents // @Tags Agents
// @Param request body codersdk.PostWorkspaceAgentVersionRequest true "Version request" // @Param request body codersdk.PostWorkspaceAgentVersionRequest true "Version request"
// @Success 200 // @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. // workspaceAgentPTY spawns a PTY and pipes it over a WebSocket.
// This is used for the web terminal. // 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) { func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
@ -276,6 +292,14 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
agent.Bicopy(ctx, wsNetConn, ptNetConn) 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) { func (api *API) workspaceAgentListeningPorts(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
workspace := httpmw.WorkspaceParam(r) workspace := httpmw.WorkspaceParam(r)
@ -443,6 +467,14 @@ func (api *API) dialWorkspaceAgentTailnet(r *http.Request, agentID uuid.UUID) (*
}, nil }, 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) { func (api *API) workspaceAgentConnection(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
workspace := httpmw.WorkspaceParam(r) workspace := httpmw.WorkspaceParam(r)
@ -458,9 +490,8 @@ func (api *API) workspaceAgentConnection(rw http.ResponseWriter, r *http.Request
// @Summary Coordinate workspace agent via Tailnet // @Summary Coordinate workspace agent via Tailnet
// @Description It accepts a WebSocket connection to an agent that listens to // @Description It accepts a WebSocket connection to an agent that listens to
// @Description incoming connections and publishes node updates. // @Description incoming connections and publishes node updates.
// @ID get-workspace-agent-git-ssh-key-via-tailnet // @ID coordinate-workspace-agent-via-tailnet
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json
// @Tags Agents // @Tags Agents
// @Success 101 // @Success 101
// @Router /workspaceagents/me/coordinate [get] // @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. // workspaceAgentClientCoordinate accepts a WebSocket that reads node network updates.
// After accept a PubSub starts listening for new connection node updates // After accept a PubSub starts listening for new connection node updates
// which are written to the WebSocket. // 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) { func (api *API) workspaceAgentClientCoordinate(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
@ -784,8 +823,9 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin
} }
// @Summary Submit workspace agent stats // @Summary Submit workspace agent stats
// @ID submit-workspace-workspace-agent-stats // @ID submit-workspace-agent-stats
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce application/json // @Produce application/json
// @Tags Agents // @Tags Agents
// @Param request body codersdk.AgentStats true "Stats request" // @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 // @Summary Submit workspace agent application health
// @ID submit-workspace-workspace-agent-health // @ID submit-workspace-agent-application-health
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce application/json // @Produce application/json
// @Tags Agents // @Tags Agents
// @Param request body codersdk.PostWorkspaceAppHealthsRequest true "Application health request" // @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 // @Summary Get workspace agent Git auth
// @ID get-workspace-agent-git-auth // @ID get-workspace-agent-git-auth
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Agents // @Tags Agents
// @Param url query string true "Git URL" format(uri) // @Param url query string true "Git URL" format(uri)

View File

@ -66,7 +66,7 @@ var nonCanonicalHeaders = map[string]string{
} }
// @Summary Get applications host // @Summary Get applications host
// @ID get-app-host // @ID get-applications-host
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Applications // @Tags Applications
@ -614,6 +614,13 @@ func (api *API) setWorkspaceAppCookie(rw http.ResponseWriter, r *http.Request, t
return true 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 // @Summary Redirect to URI with encrypted API key
// @ID redirect-to-uri-with-encrypted-api-key // @ID redirect-to-uri-with-encrypted-api-key
// @Security CoderSessionToken // @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" // @Param redirect_uri query string false "Redirect destination"
// @Success 307 // @Success 307
// @Router /applications/auth-redirect [get] // @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) { func (api *API) workspaceApplicationAuth(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
if api.AppHostname == "" { if api.AppHostname == "" {

View File

@ -77,13 +77,13 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Builds // @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 after_id query string false "After ID" format(uuid)
// @Param limit query int false "Page limit" // @Param limit query int false "Page limit"
// @Param offset query int false "Page offset" // @Param offset query int false "Page offset"
// @Param since query string false "Since timestamp" format(date-time) // @Param since query string false "Since timestamp" format(date-time)
// @Success 200 {array} codersdk.WorkspaceBuild // @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) { func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
workspace := httpmw.WorkspaceParam(r) workspace := httpmw.WorkspaceParam(r)
@ -290,13 +290,13 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ
// @Summary Create workspace build // @Summary Create workspace build
// @ID create-workspace-build // @ID create-workspace-build
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accepts json // @Accept json
// @Produce json // @Produce json
// @Tags Builds // @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" // @Param request body codersdk.CreateWorkspaceBuildRequest true "Create workspace build request"
// @Success 200 {object} codersdk.WorkspaceBuild // @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) { func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
apiKey := httpmw.APIKey(r) apiKey := httpmw.APIKey(r)

View File

@ -23,6 +23,7 @@ import (
// @Summary Authenticate agent on Azure instance // @Summary Authenticate agent on Azure instance
// @ID authenticate-agent-on-azure-instance // @ID authenticate-agent-on-azure-instance
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Agents // @Tags Agents
// @Param request body codersdk.AzureInstanceIdentityToken true "Instance identity token" // @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) 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 // @Summary Authenticate agent on AWS instance
// @ID authenticate-agent-on-aws-instance // @ID authenticate-agent-on-aws-instance
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Agents // @Tags Agents
// @Param request body codersdk.AWSInstanceIdentityToken true "Instance identity token" // @Param request body codersdk.AWSInstanceIdentityToken true "Instance identity token"
// @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse // @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse
// @Router /workspaceagents/aws-instance-identity [post] // @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) { func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
var req codersdk.AWSInstanceIdentityToken var req codersdk.AWSInstanceIdentityToken
@ -74,18 +76,19 @@ func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r *
api.handleAuthInstanceID(rw, r, identity.InstanceID) 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 // @Summary Authenticate agent on Google Cloud instance
// @ID authenticate-agent-on-google-cloud-instance // @ID authenticate-agent-on-google-cloud-instance
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Agents // @Tags Agents
// @Param request body codersdk.GoogleInstanceIdentityToken true "Instance identity token" // @Param request body codersdk.GoogleInstanceIdentityToken true "Instance identity token"
// @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse // @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse
// @Router /workspaceagents/google-instance-identity [post] // @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) { func (api *API) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
var req codersdk.GoogleInstanceIdentityToken var req codersdk.GoogleInstanceIdentityToken

View File

@ -48,10 +48,10 @@ var (
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Workspaces // @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" // @Param include_deleted query bool false "Return data instead of HTTP 404 if the workspace is deleted"
// @Success 200 {object} codersdk.Workspace // @Success 200 {object} codersdk.Workspace
// @Router /workspaces/{id} [get] // @Router /workspaces/{workspace} [get]
func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
workspace := httpmw.WorkspaceParam(r) 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 // @Summary List workspaces
// @ID get-workspaces // @ID list-workspaces
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Workspaces // @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) // @Param has_agent query string false "Filter by agent status" Enums(connected,connecting,disconnected,timeout)
// @Success 200 {object} codersdk.WorkspacesResponse // @Success 200 {object} codersdk.WorkspacesResponse
// @Router /workspaces [get] // @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) { func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
apiKey := httpmw.APIKey(r) 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 // @Summary Create user workspace by organization
// @ID create-user-workspace-by-organization // @ID create-user-workspace-by-organization
// @Security CoderSessionToken // @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" // @Param user path string true "Username, UUID, or me"
// @Success 200 {object} codersdk.Workspace // @Success 200 {object} codersdk.Workspace
// @Router /organizations/{organization}/members/{user}/workspaces [post] // @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) { func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() ctx = r.Context()
@ -558,7 +558,6 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
// @ID update-workspace-metadata-by-id // @ID update-workspace-metadata-by-id
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json // @Accept json
// @Produce json
// @Tags Workspaces // @Tags Workspaces
// @Param workspace path string true "Workspace ID" format(uuid) // @Param workspace path string true "Workspace ID" format(uuid)
// @Param request body codersdk.UpdateWorkspaceRequest true "Metadata update request" // @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 // @ID update-workspace-autostart-schedule-by-id
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json // @Accept json
// @Produce json
// @Tags Workspaces // @Tags Workspaces
// @Param workspace path string true "Workspace ID" format(uuid) // @Param workspace path string true "Workspace ID" format(uuid)
// @Param request body codersdk.UpdateWorkspaceAutostartRequest true "Schedule update request" // @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 // @ID update-workspace-ttl-by-id
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json // @Accept json
// @Produce json
// @Tags Workspaces // @Tags Workspaces
// @Param workspace path string true "Workspace ID" format(uuid) // @Param workspace path string true "Workspace ID" format(uuid)
// @Param request body codersdk.UpdateWorkspaceTTLRequest true "Workspace TTL update request" // @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 // @Summary Watch workspace by ID
// @ID watch-workspace-id // @ID watch-workspace-by-id
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce text/event-stream // @Produce text/event-stream
// @Tags Workspaces // @Tags Workspaces

View File

@ -88,14 +88,14 @@ type AuditDiffField struct {
} }
type AuditLog struct { type AuditLog struct {
ID uuid.UUID `json:"id"` ID uuid.UUID `json:"id" format:"uuid"`
RequestID uuid.UUID `json:"request_id"` RequestID uuid.UUID `json:"request_id" format:"uuid"`
Time time.Time `json:"time"` Time time.Time `json:"time" format:"date-time"`
OrganizationID uuid.UUID `json:"organization_id"` OrganizationID uuid.UUID `json:"organization_id" format:"uuid"`
IP netip.Addr `json:"ip"` IP netip.Addr `json:"ip"`
UserAgent string `json:"user_agent"` UserAgent string `json:"user_agent"`
ResourceType ResourceType `json:"resource_type"` 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 is the name of the resource.
ResourceTarget string `json:"resource_target"` ResourceTarget string `json:"resource_target"`
ResourceIcon string `json:"resource_icon"` ResourceIcon string `json:"resource_icon"`
@ -123,8 +123,8 @@ type AuditLogResponse struct {
type CreateTestAuditLogRequest struct { type CreateTestAuditLogRequest struct {
Action AuditAction `json:"action,omitempty" enums:"create,write,delete,start,stop"` 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"` 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"` ResourceID uuid.UUID `json:"resource_id,omitempty" format:"uuid"`
Time time.Time `json:"time,omitempty"` Time time.Time `json:"time,omitempty" format:"date-time"`
} }
// AuditLogs retrieves audit logs from the given page. // AuditLogs retrieves audit logs from the given page.

View File

@ -67,7 +67,7 @@ type CreateTemplateRequest struct {
// This is required on creation to enable a user-flow of validating a // 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 // template works. There is no reason the data-model cannot support empty
// templates, but it doesn't make sense for users. // 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"` ParameterValues []CreateParameterRequest `json:"parameter_values,omitempty"`
// DefaultTTLMillis allows optionally specifying the default TTL // DefaultTTLMillis allows optionally specifying the default TTL
@ -81,7 +81,7 @@ type CreateTemplateRequest struct {
// CreateWorkspaceRequest provides options for creating a new workspace. // CreateWorkspaceRequest provides options for creating a new workspace.
type CreateWorkspaceRequest struct { 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"` Name string `json:"name" validate:"workspace_name,required"`
AutostartSchedule *string `json:"autostart_schedule"` AutostartSchedule *string `json:"autostart_schedule"`
TTLMillis *int64 `json:"ttl_ms,omitempty"` TTLMillis *int64 `json:"ttl_ms,omitempty"`

View File

@ -14,7 +14,7 @@ type Pagination struct {
// Offset for better performance. To use it as an alternative, // Offset for better performance. To use it as an alternative,
// set AfterID to the last UUID returned by the previous // set AfterID to the last UUID returned by the previous
// request. // 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 // Limit sets the maximum number of users to be returned
// in a single page. If the limit is <= 0, there is no limit // in a single page. If the limit is <= 0, there is no limit
// and all users are returned. // and all users are returned.

View File

@ -176,7 +176,7 @@ func (c *Client) UpdateActiveTemplateVersion(ctx context.Context, template uuid.
// TemplateVersionsByTemplateRequest defines the request parameters for // TemplateVersionsByTemplateRequest defines the request parameters for
// TemplateVersionsByTemplate. // TemplateVersionsByTemplate.
type TemplateVersionsByTemplateRequest struct { type TemplateVersionsByTemplateRequest struct {
TemplateID uuid.UUID `json:"template_id" validate:"required"` TemplateID uuid.UUID `json:"template_id" validate:"required" format:"uuid"`
Pagination Pagination
} }
@ -210,7 +210,7 @@ func (c *Client) TemplateVersionByName(ctx context.Context, template uuid.UUID,
} }
type DAUEntry struct { type DAUEntry struct {
Date time.Time `json:"date"` Date time.Time `json:"date" format:"date-time"`
Amount int `json:"amount"` Amount int `json:"amount"`
} }

View File

@ -47,7 +47,7 @@ type WorkspacesResponse struct {
// CreateWorkspaceBuildRequest provides options to update the latest workspace build. // CreateWorkspaceBuildRequest provides options to update the latest workspace build.
type CreateWorkspaceBuildRequest struct { 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"` Transition WorkspaceTransition `json:"transition" validate:"oneof=create start stop delete,required"`
DryRun bool `json:"dry_run,omitempty"` DryRun bool `json:"dry_run,omitempty"`
ProvisionerState []byte `json:"state,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 // PutExtendWorkspaceRequest is a request to extend the deadline of
// the active workspace build. // the active workspace build.
type PutExtendWorkspaceRequest struct { 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. // PutExtendWorkspace updates the deadline for resources of the latest workspace build.

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). To perform this operation, you must be authenticated. [Learn more](authentication.md).
## Submit workspace application health ## Submit workspace agent application health
### Code samples ### 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) | | 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). 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).

View File

@ -47,18 +47,18 @@ curl -X GET http://coder-server:8080/api/v2/audit?q=string \
"secret": true "secret": true
} }
}, },
"id": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"ip": "string", "ip": "string",
"is_deleted": true, "is_deleted": true,
"organization_id": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"request_id": "string", "request_id": "266ea41d-adf5-480b-af50-15b940c2b846",
"resource_icon": "string", "resource_icon": "string",
"resource_id": "string", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f",
"resource_link": "string", "resource_link": "string",
"resource_target": "string", "resource_target": "string",
"resource_type": "organization", "resource_type": "organization",
"status_code": 0, "status_code": 0,
"time": "string", "time": "2019-08-24T14:15:22Z",
"user": { "user": {
"avatar_url": "http://example.com", "avatar_url": "http://example.com",
"created_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z",
@ -108,9 +108,9 @@ curl -X POST http://coder-server:8080/api/v2/audit/testgenerate \
```json ```json
{ {
"action": "create", "action": "create",
"resource_id": "string", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f",
"resource_type": "organization", "resource_type": "organization",
"time": "string" "time": "2019-08-24T14:15:22Z"
} }
``` ```

View File

@ -740,22 +740,22 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`GET /workspaces/{id}/builds` `GET /workspaces/{workspace}/builds`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ---------- | ----- | ----------------- | -------- | --------------- | | ----------- | ----- | ----------------- | -------- | --------------- |
| `id` | path | string(uuid) | true | Workspace ID | | `workspace` | path | string(uuid) | true | Workspace ID |
| `after_id` | query | string(uuid) | false | After ID | | `after_id` | query | string(uuid) | false | After ID |
| `limit` | query | integer | false | Page limit | | `limit` | query | integer | false | Page limit |
| `offset` | query | integer | false | Page offset | | `offset` | query | integer | false | Page offset |
| `since` | query | string(date-time) | false | Since timestamp | | `since` | query | string(date-time) | false | Since timestamp |
### Example responses ### Example responses
@ -1019,13 +1019,13 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Content-Type: application/json' \
-H 'Accept: application/json' \ -H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`POST /workspaces/{id}/builds` `POST /workspaces/{workspace}/builds`
> Body parameter > Body parameter
@ -1043,17 +1043,17 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{id}/builds \
} }
], ],
"state": [0], "state": [0],
"template_version_id": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1",
"transition": "create" "transition": "create"
} }
``` ```
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ------ | ---- | -------------------------------------------------------------------------------------- | -------- | ------------------------------ | | ----------- | ---- | -------------------------------------------------------------------------------------- | -------- | ------------------------------ |
| `id` | path | string(uuid) | true | Workspace ID | | `workspace` | path | string(uuid) | true | Workspace ID |
| `body` | body | [codersdk.CreateWorkspaceBuildRequest](schemas.md#codersdkcreateworkspacebuildrequest) | true | Create workspace build request | | `body` | body | [codersdk.CreateWorkspaceBuildRequest](schemas.md#codersdkcreateworkspacebuildrequest) | true | Create workspace build request |
### Example responses ### Example responses

View File

@ -242,18 +242,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`GET /groups/{groupName}` `GET /groups/{group}`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ----------- | ---- | ------ | -------- | ----------- | | ------- | ---- | ------ | -------- | ----------- |
| `groupName` | path | string | true | Group name | | `group` | path | string | true | Group name |
### Example responses ### 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). To perform this operation, you must be authenticated. [Learn more](authentication.md).
## Delete license ## Delete group by name
### Code samples ### Code samples
```shell ```shell
# Example request using curl # 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' -H 'Coder-Session-Token: API_KEY'
``` ```
`DELETE /license/{id}` `DELETE /groups/{group}`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ---- | ---- | -------------- | -------- | ----------- | | ------- | ---- | ------ | -------- | ----------- |
| `id` | path | string(number) | true | License ID | | `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 ### Responses
| Status | Meaning | Description | Schema | | Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | ------ | | ------ | ------------------------------------------------------- | ----------- | ------------------------------------------ |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | | | 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). 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). 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 ## Get groups by organization
### Code samples ### Code samples
@ -462,6 +580,66 @@ Status Code **200**
To perform this operation, you must be authenticated. [Learn more](authentication.md). 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 ## Get provisioner daemons
### Code samples ### Code samples
@ -609,6 +787,26 @@ Status Code **200**
To perform this operation, you must be authenticated. [Learn more](authentication.md). 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 ## SCIM 2.0: Create new user
### Code samples ### Code samples

View File

@ -323,18 +323,18 @@
"secret": true "secret": true
} }
}, },
"id": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"ip": "string", "ip": "string",
"is_deleted": true, "is_deleted": true,
"organization_id": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"request_id": "string", "request_id": "266ea41d-adf5-480b-af50-15b940c2b846",
"resource_icon": "string", "resource_icon": "string",
"resource_id": "string", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f",
"resource_link": "string", "resource_link": "string",
"resource_target": "string", "resource_target": "string",
"resource_type": "organization", "resource_type": "organization",
"status_code": 0, "status_code": 0,
"time": "string", "time": "2019-08-24T14:15:22Z",
"user": { "user": {
"avatar_url": "http://example.com", "avatar_url": "http://example.com",
"created_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z",
@ -399,18 +399,18 @@
"secret": true "secret": true
} }
}, },
"id": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"ip": "string", "ip": "string",
"is_deleted": true, "is_deleted": true,
"organization_id": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"request_id": "string", "request_id": "266ea41d-adf5-480b-af50-15b940c2b846",
"resource_icon": "string", "resource_icon": "string",
"resource_id": "string", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f",
"resource_link": "string", "resource_link": "string",
"resource_target": "string", "resource_target": "string",
"resource_type": "organization", "resource_type": "organization",
"status_code": 0, "status_code": 0,
"time": "string", "time": "2019-08-24T14:15:22Z",
"user": { "user": {
"avatar_url": "http://example.com", "avatar_url": "http://example.com",
"created_at": "2019-08-24T14:15:22Z", "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" "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 ```json
{ {
"action": "create", "action": "create",
"resource_id": "string", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f",
"resource_type": "organization", "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], "state": [0],
"template_version_id": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1",
"transition": "create" "transition": "create"
} }
``` ```
@ -901,7 +901,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
```json ```json
{ {
"amount": 0, "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 | | | | `uploaded_at` | string | false | | |
| `uuid` | 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 ## codersdk.LogLevel
```json ```json
@ -3463,7 +3515,7 @@ Parameter represents a set value for the scope.
```json ```json
{ {
"deadline": "string" "deadline": "2019-08-24T14:15:22Z"
} }
``` ```
@ -3907,7 +3959,7 @@ Parameter represents a set value for the scope.
"entries": [ "entries": [
{ {
"amount": 0, "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 | | | | `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 ## codersdk.WorkspaceAgentGitAuthResponse
```json ```json

View File

@ -204,7 +204,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
"source_value": "string" "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 ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`GET /templates/{id}` `GET /templates/{template}`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ---- | ---- | ------------ | -------- | ----------- | | ---------- | ---- | ------------ | -------- | ----------- |
| `id` | path | string(uuid) | true | Template ID | | `template` | path | string(uuid) | true | Template ID |
### Example responses ### Example responses
@ -692,18 +692,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`DELETE /templates/{id}` `DELETE /templates/{template}`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ---- | ---- | ------------ | -------- | ----------- | | ---------- | ---- | ------------ | -------- | ----------- |
| `id` | path | string(uuid) | true | Template ID | | `template` | path | string(uuid) | true | Template ID |
### Example responses ### Example responses
@ -736,18 +736,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`PATCH /templates/{id}` `PATCH /templates/{template}`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ---- | ---- | ------------ | -------- | ----------- | | ---------- | ---- | ------------ | -------- | ----------- |
| `id` | path | string(uuid) | true | Template ID | | `template` | path | string(uuid) | true | Template ID |
### Example responses ### Example responses
@ -798,18 +798,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`GET /templates/{id}/daus` `GET /templates/{template}/daus`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ---- | ---- | ------------ | -------- | ----------- | | ---------- | ---- | ------------ | -------- | ----------- |
| `id` | path | string(uuid) | true | Template ID | | `template` | path | string(uuid) | true | Template ID |
### Example responses ### Example responses
@ -820,7 +820,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{id}/daus \
"entries": [ "entries": [
{ {
"amount": 0, "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 ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`GET /templates/{id}/versions` `GET /templates/{template}/versions`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | 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 | | `after_id` | query | string(uuid) | false | After ID |
| `limit` | query | integer | false | Page limit | | `limit` | query | integer | false | Page limit |
| `offset` | query | integer | false | Page offset | | `offset` | query | integer | false | Page offset |
@ -971,13 +971,13 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Content-Type: application/json' \
-H 'Accept: application/json' \ -H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`PATCH /templates/{id}/versions` `PATCH /templates/{template}/versions`
> Body parameter > Body parameter
@ -989,10 +989,10 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{id}/versions \
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ------ | ---- | -------------------------------------------------------------------------------------- | -------- | ------------------------- | | ---------- | ---- | -------------------------------------------------------------------------------------- | -------- | ------------------------- |
| `id` | path | string(uuid) | true | Template ID | | `template` | path | string(uuid) | true | Template ID |
| `body` | body | [codersdk.UpdateActiveTemplateVersion](schemas.md#codersdkupdateactivetemplateversion) | true | Modified template version | | `body` | body | [codersdk.UpdateActiveTemplateVersion](schemas.md#codersdkupdateactivetemplateversion) | true | Modified template version |
### Example responses ### Example responses
@ -1025,18 +1025,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`GET /templates/{id}/versions/{templateversionname}` `GET /templates/{template}/versions/{templateversionname}`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | 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 | | `templateversionname` | path | string | true | Template version name |
### Example responses ### Example responses
@ -1340,19 +1340,19 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`GET /templateversions/{templateversion}/dry-run/{jobid}` `GET /templateversions/{templateversion}/dry-run/{jobID}`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ----------------- | ---- | ------------ | -------- | ------------------- | | ----------------- | ---- | ------------ | -------- | ------------------- |
| `templateversion` | path | string(uuid) | true | Template version ID | | `templateversion` | path | string(uuid) | true | Template version ID |
| `jobid` | path | string(uuid) | true | Job ID | | `jobID` | path | string(uuid) | true | Job ID |
### Example responses ### Example responses
@ -1390,17 +1390,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`PATCH /templateversions/{templateversion}/dry-run/{jobid}/cancel` `PATCH /templateversions/{templateversion}/dry-run/{jobID}/cancel`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ----------------- | ---- | ------------ | -------- | ------------------- | | ----------------- | ---- | ------------ | -------- | ------------------- |
| `jobID` | path | string(uuid) | true | Job ID |
| `templateversion` | path | string(uuid) | true | Template version ID | | `templateversion` | path | string(uuid) | true | Template version ID |
### Example responses ### Example responses
@ -1434,19 +1435,19 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`GET /templateversions/{templateversion}/dry-run/{jobid}/logs` `GET /templateversions/{templateversion}/dry-run/{jobID}/logs`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ----------------- | ----- | ------------ | -------- | --------------------- | | ----------------- | ----- | ------------ | -------- | --------------------- |
| `templateversion` | path | string(uuid) | true | Template version ID | | `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 | | `before` | query | integer | false | Before Unix timestamp |
| `after` | query | integer | false | After Unix timestamp | | `after` | query | integer | false | After Unix timestamp |
| `follow` | query | boolean | false | Follow log stream | | `follow` | query | boolean | false | Follow log stream |
@ -1508,19 +1509,19 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`GET /templateversions/{templateversion}/dry-run/{jobid}/resources` `GET /templateversions/{templateversion}/dry-run/{jobID}/resources`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | Name | In | Type | Required | Description |
| ----------------- | ---- | ------------ | -------- | ------------------- | | ----------------- | ---- | ------------ | -------- | ------------------- |
| `templateversion` | path | string(uuid) | true | Template version ID | | `templateversion` | path | string(uuid) | true | Template version ID |
| `jobid` | path | string(uuid) | true | Job ID | | `jobID` | path | string(uuid) | true | Job ID |
### Example responses ### Example responses

View File

@ -511,18 +511,18 @@ To perform this operation, you must be authenticated. [Learn more](authenticatio
```shell ```shell
# Example request using curl # 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 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY' -H 'Coder-Session-Token: API_KEY'
``` ```
`GET /workspaces/{id}` `GET /workspaces/{workspace}`
### Parameters ### Parameters
| Name | In | Type | Required | Description | | 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 | | `include_deleted` | query | boolean | false | Return data instead of HTTP 404 if the workspace is deleted |
### Example responses ### Example responses
@ -755,7 +755,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/extend \
```json ```json
{ {
"deadline": "string" "deadline": "2019-08-24T14:15:22Z"
} }
``` ```

View File

@ -84,6 +84,7 @@ func validateHexColor(color string) error {
// @Summary Update appearance // @Summary Update appearance
// @ID update-appearance // @ID update-appearance
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Enterprise // @Tags Enterprise
// @Param request body codersdk.AppearanceConfig true "Update appearance request" // @Param request body codersdk.AppearanceConfig true "Update appearance request"

View File

@ -87,7 +87,7 @@ func New(ctx context.Context, options *Options) (*API, error) {
httpmw.ExtractGroupByNameParam(api.Database), httpmw.ExtractGroupByNameParam(api.Database),
) )
r.Get("/", api.group) r.Get("/", api.groupByOrganization)
}) })
}) })
r.Route("/organizations/{organization}/provisionerdaemons", func(r chi.Router) { r.Route("/organizations/{organization}/provisionerdaemons", func(r chi.Router) {

View File

@ -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)
}

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)) 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) { func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() 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)) 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) { func (api *API) deleteGroup(rw http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() 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 // @Summary Get group by name
// @ID get-group-by-name // @ID get-group-by-name
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json // @Produce json
// @Tags Enterprise // @Tags Enterprise
// @Param groupName path string true "Group name" // @Param group path string true "Group name"
// @Success 200 {object} codersdk.Group // @Success 200 {object} codersdk.Group
// @Router /groups/{groupName} [get] // @Router /groups/{group} [get]
func (api *API) group(rw http.ResponseWriter, r *http.Request) { func (api *API) group(rw http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() ctx = r.Context()

View File

@ -52,6 +52,7 @@ var Keys = map[string]ed25519.PublicKey{"2022-08-12": ed25519.PublicKey(key20220
// @Summary Add new license // @Summary Add new license
// @ID add-new-license // @ID add-new-license
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Organizations // @Tags Organizations
// @Param request body codersdk.AddLicenseRequest true "Add license request" // @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 // @Tags Enterprise
// @Param id path string true "License ID" format(number) // @Param id path string true "License ID" format(number)
// @Success 200 // @Success 200
// @Router /license/{id} [delete] // @Router /licenses/{id} [delete]
func (api *API) deleteLicense(rw http.ResponseWriter, r *http.Request) { func (api *API) deleteLicense(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
if !api.AGPL.Authorize(r, rbac.ActionDelete, rbac.ResourceLicense) { if !api.AGPL.Authorize(r, rbac.ActionDelete, rbac.ResourceLicense) {

View File

@ -94,7 +94,6 @@ func (api *API) provisionerDaemons(rw http.ResponseWriter, r *http.Request) {
// @Summary Serve provisioner daemon // @Summary Serve provisioner daemon
// @ID serve-provisioner-daemon // @ID serve-provisioner-daemon
// @Security CoderSessionToken // @Security CoderSessionToken
// @Produce json
// @Tags Enterprise // @Tags Enterprise
// @Param organization path string true "Organization ID" format(uuid) // @Param organization path string true "Organization ID" format(uuid)
// @Success 101 // @Success 101

View File

@ -49,7 +49,7 @@ func (api *API) scimVerifyAuthHeader(r *http.Request) bool {
// @Produce application/scim+json // @Produce application/scim+json
// @Tags Enterprise // @Tags Enterprise
// @Success 200 // @Success 200
// @Router /scim/v2/Users [post] // @Router /scim/v2/Users [get]
// //
//nolint:revive //nolint:revive
func (api *API) scimGetUsers(rw http.ResponseWriter, r *http.Request) { func (api *API) scimGetUsers(rw http.ResponseWriter, r *http.Request) {

View File

@ -103,6 +103,7 @@ func (api *API) templateACL(rw http.ResponseWriter, r *http.Request) {
// @Summary Update template ACL // @Summary Update template ACL
// @ID update-template-acl // @ID update-template-acl
// @Security CoderSessionToken // @Security CoderSessionToken
// @Accept json
// @Produce json // @Produce json
// @Tags Enterprise // @Tags Enterprise
// @Param template path string true "Template ID" format(uuid) // @Param template path string true "Template ID" format(uuid)