feat: Build framework for generating API docs (#5383)

* WIP

* Gen

* WIP

* chi swagger

* WIP

* WIP

* WIP

* GetWorkspaces

* GetWorkspaces

* Markdown

* Use widdershins

* WIP

* WIP

* WIP

* Markdown template

* Fix: makefile

* fmt

* Fix: comment

* Enable swagger conditionally

* fix: site

* Default false

* Flag tests

* fix

* fix

* template fixes

* Fix

* Fix

* Fix

* WIP

* Formatted

* Cleanup

* Templates

* BEGIN END SECTION

* subshell exit code

* Fix

* Fix merge

* WIP

* Fix

* Fix fmt

* Fix

* Generic api.md page

* Fix merge

* Link pages

* Fix

* Fix

* Fix: links

* Add icon

* Write manifest file

* Fix fmt

* Fix: enterprise

* Fix: Swagger.Enable

* Fix: rename apidocs to apidoc

* Fix: find -not -prune

* Fix: json not available

* Fix: rename Coderd API to Coder API

* Fix: npm exec

* Fix: api dir

* Fix: by ID

* Fix: string uuid

* Fix: include deleted

* Fix: indirect go.mod

* Fix: source lib.sh

* Fix: shellcheck

* Fix: pushd popd

* Fix: fmt

* Fix: improve workspaces

* Fix: swagger-enable

* Fix

* Fix: mention only HTTP 200

* Fix: IDs

* Fix: https

* Fix: icon

* More APis

* Fix: format swagger.json

* Fix: SwaggerEndpoint

* Fix: SCRIPT_DIR

* Fix: PROJECT_ROOT

* Fix: use code tags in schemas.md

* Fix: examples

* Fix: examples

* Fix: improve format

* Fix: date-time,enums

* Fix: include_deleted

* Fix: array of

* Fix: parameter, response

* Fix: string time or null

* Workspaces: more docs

* Workspaces: more docs

* Fix: renderDisplayName

* Fix: ActiveUserCount

* Fix

* Fix: typo

* Templates: docs

* Notice: incomplete
This commit is contained in:
Marcin Tojek
2022-12-19 18:43:46 +01:00
committed by GitHub
parent f239ca7ee3
commit dc6d271293
41 changed files with 9272 additions and 76 deletions
+8 -2
View File
@@ -410,13 +410,14 @@ gen: \
provisionersdk/proto/provisioner.pb.go \
provisionerd/proto/provisionerd.pb.go \
site/src/api/typesGenerated.ts \
docs/admin/prometheus.md
docs/admin/prometheus.md \
coderd/apidoc/swagger.json
.PHONY: gen
# Mark all generated files as fresh so make thinks they're up-to-date. This is
# used during releases so we don't run generation scripts.
gen/mark-fresh:
files="coderd/database/dump.sql coderd/database/querier.go provisionersdk/proto/provisioner.pb.go provisionerd/proto/provisionerd.pb.go site/src/api/typesGenerated.ts docs/admin/prometheus.md"
files="coderd/database/dump.sql coderd/database/querier.go provisionersdk/proto/provisioner.pb.go provisionerd/proto/provisionerd.pb.go site/src/api/typesGenerated.ts docs/admin/prometheus.md coderd/apidoc/swagger.json"
for file in $$files; do
echo "$$file"
if [ ! -f "$$file" ]; then
@@ -464,6 +465,11 @@ docs/admin/prometheus.md: scripts/metricsdocgen/main.go scripts/metricsdocgen/me
cd site
yarn run format:write ../docs/admin/prometheus.md
coderd/apidoc/swagger.json: $(shell find ./scripts/apidocgen -not \( -path './scripts/apidocgen/node_modules' -prune \) -type f) $(wildcard coderd/*.go) $(wildcard codersdk/*.go)
./scripts/apidocgen/generate.sh
cd site
yarn run format:write ../docs/api ../docs/manifest.json ../coderd/apidoc/swagger.json
update-golden-files: cli/testdata/.gen-golden
.PHONY: update-golden-files
+8
View File
@@ -452,6 +452,14 @@ func newConfig() *codersdk.DeploymentConfig {
Flag: "max-token-lifetime",
Default: 24 * 30 * time.Hour,
},
Swagger: &codersdk.SwaggerConfig{
Enable: &codersdk.DeploymentConfigField[bool]{
Name: "Enable swagger endpoint",
Usage: "Expose the swagger endpoint via /swagger.",
Flag: "swagger-enable",
Default: false,
},
},
}
}
+4
View File
@@ -678,6 +678,10 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
), cfg.Prometheus.Address.Value, "prometheus")()
}
if cfg.Swagger.Enable.Value {
options.SwaggerEndpoint = cfg.Swagger.Enable.Value
}
// We use a separate coderAPICloser so the Enterprise API
// can have it's own close functions. This is cleaner
// than abstracting the Coder API itself.
+2
View File
@@ -169,6 +169,8 @@ Flags:
"ecdsa", or "rsa4096".
Consumes $CODER_SSH_KEYGEN_ALGORITHM
(default "ed25519")
--swagger-enable Expose the swagger endpoint via /swagger.
Consumes $CODER_SWAGGER_ENABLE
--telemetry Whether telemetry is enabled or not.
Coder collects anonymized usage data to
help improve our product.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+31 -1
View File
@@ -22,6 +22,7 @@ import (
"github.com/klauspost/compress/zstd"
"github.com/moby/moby/pkg/namesgenerator"
"github.com/prometheus/client_golang/prometheus"
httpSwagger "github.com/swaggo/http-swagger"
"go.opentelemetry.io/otel/trace"
"golang.org/x/xerrors"
"google.golang.org/api/idtoken"
@@ -34,6 +35,9 @@ import (
"cdr.dev/slog"
"github.com/coder/coder/buildinfo"
// Used to serve the Swagger endpoint
_ "github.com/coder/coder/coderd/apidoc"
"github.com/coder/coder/coderd/audit"
"github.com/coder/coder/coderd/awsidentity"
"github.com/coder/coder/coderd/database"
@@ -102,15 +106,34 @@ type Options struct {
TailnetCoordinator tailnet.Coordinator
DERPServer *derp.Server
DERPMap *tailcfg.DERPMap
SwaggerEndpoint bool
MetricsCacheRefreshInterval time.Duration
AgentStatsRefreshInterval time.Duration
Experimental bool
DeploymentConfig *codersdk.DeploymentConfig
UpdateCheckOptions *updatecheck.Options // Set non-nil to enable update checking.
HTTPClient *http.Client
HTTPClient *http.Client
}
// @title Coder API
// @version 2.0
// @description Coderd is the service created by running coder server. It is a thin API that connects workspaces, provisioners and users. coderd stores its state in Postgres and is the only service that communicates with Postgres.
// @termsOfService https://coder.com/legal/terms-of-service
// @contact.name API Support
// @contact.url https://coder.com
// @contact.email support@coder.com
// @license.name AGPL-3.0
// @license.url https://github.com/coder/coder/blob/main/LICENSE
// @BasePath /api/v2
// @securitydefinitions.apiKey CoderSessionToken
// @in header
// @name Coder-Session-Token
// New constructs a Coder API handler.
func New(options *Options) *API {
if options == nil {
@@ -578,6 +601,13 @@ func New(options *Options) *API {
})
})
if options.SwaggerEndpoint {
// Swagger UI requires the URL trailing slash. Otherwise, the browser tries to load /assets
// from http://localhost:8080/assets instead of http://localhost:8080/swagger/assets.
r.Get("/swagger", http.RedirectHandler("/swagger/", http.StatusTemporaryRedirect).ServeHTTP)
r.Get("/swagger/*", httpSwagger.Handler(httpSwagger.URL("/swagger/doc.json")))
}
r.NotFound(compressHandler(http.HandlerFunc(api.siteHandler.ServeHTTP)).ServeHTTP)
return api
}
+78
View File
@@ -129,3 +129,81 @@ func TestHealthz(t *testing.T) {
assert.Equal(t, "OK", string(body))
}
func TestSwagger(t *testing.T) {
t.Parallel()
const swaggerEndpoint = "/swagger"
t.Run("endpoint enabled", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, &coderdtest.Options{
SwaggerEndpoint: true,
})
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()
resp, err := requestWithRetries(ctx, t, client, http.MethodGet, swaggerEndpoint, nil)
require.NoError(t, err)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
defer resp.Body.Close()
require.Contains(t, string(body), "Swagger UI")
})
t.Run("doc.json exposed", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, &coderdtest.Options{
SwaggerEndpoint: true,
})
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()
resp, err := requestWithRetries(ctx, t, client, http.MethodGet, swaggerEndpoint+"/doc.json", nil)
require.NoError(t, err)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
defer resp.Body.Close()
require.Contains(t, string(body), `"swagger": "2.0"`)
})
t.Run("endpoint disabled by default", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()
resp, err := requestWithRetries(ctx, t, client, http.MethodGet, swaggerEndpoint, nil)
require.NoError(t, err)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, "<pre>\n</pre>\n", string(body))
})
t.Run("doc.json disabled by default", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()
resp, err := requestWithRetries(ctx, t, client, http.MethodGet, swaggerEndpoint+"/doc.json", nil)
require.NoError(t, err)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, "<pre>\n</pre>\n", string(body))
})
}
+3
View File
@@ -115,6 +115,8 @@ type Options struct {
// test instances are running against the same database.
Database database.Store
Pubsub database.Pubsub
SwaggerEndpoint bool
}
// New constructs a codersdk client connected to an in-memory API instance.
@@ -297,6 +299,7 @@ func NewOptions(t *testing.T, options *Options) (func(http.Handler), context.Can
AgentStatsRefreshInterval: options.AgentStatsRefreshInterval,
DeploymentConfig: options.DeploymentConfig,
UpdateCheckOptions: options.UpdateCheckOptions,
SwaggerEndpoint: options.SwaggerEndpoint,
}
}
+52
View File
@@ -34,6 +34,14 @@ const (
AutoImportTemplateKubernetes AutoImportTemplate = "kubernetes"
)
// @Summary Get template metadata by ID
// @ID get-template-metadata-by-id
// @Security CoderSessionToken
// @Produce json
// @Tags Templates
// @Param id path string true "Template ID" format(uuid)
// @Success 200 {object} codersdk.Template
// @Router /templates/{id} [get]
// Returns a single template.
func (api *API) template(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
@@ -73,6 +81,14 @@ func (api *API) template(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, http.StatusOK, api.convertTemplate(template, count, createdByNameMap[template.ID.String()]))
}
// @Summary Delete template by ID
// @ID delete-template-by-id
// @Security CoderSessionToken
// @Produce json
// @Tags Templates
// @Param id path string true "Template ID" format(uuid)
// @Success 200 {object} codersdk.Response
// @Router /templates/{id} [delete]
func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
@@ -126,6 +142,17 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
})
}
// @Summary Create template by organization
// @ID create-template-by-organization
// @Security CoderSessionToken
// @Consume json
// @Produce json
// @Tags Templates
// @Param request body codersdk.CreateTemplateRequest true "Request body"
// @Param organization-id path string true "Organization ID"
// @Success 200 {object} codersdk.Template
// @Router /organizations/{organization-id}/templates/ [post]
// Returns a single template.
// Create a new template in an organization.
func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Request) {
var (
@@ -314,6 +341,14 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
httpapi.Write(ctx, rw, http.StatusCreated, template)
}
// @Summary Get templates by organization
// @ID get-templates-by-organization
// @Security CoderSessionToken
// @Produce json
// @Tags Templates
// @Param organization path string true "Organization ID" format(uuid)
// @Success 200 {object} []codersdk.Template
// @Router /organizations/{organization}/templates [get]
func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
organization := httpmw.OrganizationParam(r)
@@ -372,6 +407,15 @@ func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request)
httpapi.Write(ctx, rw, http.StatusOK, api.convertTemplates(templates, workspaceCounts, createdByNameMap))
}
// @Summary Get templates by organization and template name
// @ID get-templates-by-organization-and-template-name
// @Security CoderSessionToken
// @Produce json
// @Tags Templates
// @Param organization path string true "Organization ID" format(uuid)
// @Param template-name path string true "Template name"
// @Success 200 {object} codersdk.Template
// @Router /organizations/{organization}/templates/{template-name} [get]
func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
organization := httpmw.OrganizationParam(r)
@@ -427,6 +471,14 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re
httpapi.Write(ctx, rw, http.StatusOK, api.convertTemplate(template, count, createdByNameMap[template.ID.String()]))
}
// @Summary Update template metadata by ID
// @ID update-template-metadata
// @Security CoderSessionToken
// @Produce json
// @Tags Templates
// @Param id path string true "Template ID" format(uuid)
// @Success 200 {object} codersdk.Template
// @Router /templates/{id} [get]
func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
+89
View File
@@ -43,6 +43,15 @@ var (
errDeadlineBeforeStart = xerrors.New("new deadline must be before workspace start time")
)
// @Summary Get workspace metadata by ID
// @ID get-workspace-metadata-by-id
// @Security CoderSessionToken
// @Produce json
// @Tags Workspaces
// @Param id path string true "Workspace ID" format(uuid)
// @Param include_deleted query bool false "Return data instead of HTTP 404 if the workspace is deleted"
// @Success 200 {object} codersdk.Workspace
// @Router /workspaces/{id} [get]
func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
workspace := httpmw.WorkspaceParam(r)
@@ -92,6 +101,19 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
))
}
// @Summary List workspaces
// @ID get-workspaces
// @Security CoderSessionToken
// @Produce json
// @Tags Workspaces
// @Param owner query string false "Filter by owner username"
// @Param template query string false "Filter by template name"
// @Param name query string false "Filter with partial-match by workspace name"
// @Param status query string false "Filter by workspace status" Enums(pending,running,stopping,stopped,failed,canceling,canceled,deleted,deleting)
// @Param has_agent query string false "Filter by agent status" Enums(connected,connecting,disconnected,timeout)
// @Success 200 {object} codersdk.WorkspacesResponse
// @Router /workspaces [get]
//
// workspaces returns all workspaces a user can read.
// Optional filters with query params
func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) {
@@ -170,6 +192,16 @@ func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) {
})
}
// @Summary Get workspace metadata by owner and workspace name
// @ID get-workspace-metadata-by-owner-and-workspace-name
// @Security CoderSessionToken
// @Produce json
// @Tags Workspaces
// @Param user path string true "Owner username"
// @Param workspacename path string true "Workspace name"
// @Param include_deleted query bool false "Return data instead of HTTP 404 if the workspace is deleted"
// @Success 200 {object} codersdk.Workspace
// @Router /users/{user}/workspace/{workspacename} [get]
func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
owner := httpmw.UserParam(r)
@@ -234,6 +266,15 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
))
}
// @Summary Create workspace by organization
// @ID create-workspace-by-organization
// @Security CoderSessionToken
// @Produce json
// @Tags Workspaces
// @Param organization path string true "Organization ID" format(uuid)
// @Param user path string true "Username"
// @Success 200 {object} codersdk.Workspace
// @Router /organizations/{organization}/members/{user}/workspaces [post]
// Create a new workspace for the currently authenticated user.
func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Request) {
var (
@@ -520,6 +561,16 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
))
}
// @Summary Update workspace metadata by ID
// @ID update-workspace-metadata-by-id
// @Security CoderSessionToken
// @Consume json
// @Produce json
// @Tags Workspaces
// @Param workspace path string true "Workspace ID" format(uuid)
// @Param request body codersdk.UpdateWorkspaceRequest true "Metadata update request"
// @Success 204
// @Router /workspaces/{workspace} [patch]
func (api *API) patchWorkspace(rw http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
@@ -600,6 +651,16 @@ func (api *API) patchWorkspace(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusNoContent)
}
// @Summary Update workspace autostart schedule by ID
// @ID update-workspace-autostart-schedule-by-id
// @Security CoderSessionToken
// @Consume json
// @Produce json
// @Tags Workspaces
// @Param workspace path string true "Workspace ID" format(uuid)
// @Param request body codersdk.UpdateWorkspaceAutostartRequest true "Schedule update request"
// @Success 204
// @Router /workspaces/{workspace}/autostart [put]
func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
@@ -653,6 +714,16 @@ func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusNoContent)
}
// @Summary Update workspace TTL by ID
// @ID update-workspace-ttl-by-id
// @Security CoderSessionToken
// @Consume json
// @Produce json
// @Tags Workspaces
// @Param workspace path string true "Workspace ID" format(uuid)
// @Param request body codersdk.UpdateWorkspaceTTLRequest true "Workspace TTL update request"
// @Success 204
// @Router /workspaces/{workspace}/ttl [put]
func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
@@ -719,6 +790,16 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusNoContent)
}
// @Summary Extend workspace deadline by ID
// @ID extend-workspace-deadline-by-id
// @Security CoderSessionToken
// @Consume json
// @Produce json
// @Tags Workspaces
// @Param workspace path string true "Workspace ID" format(uuid)
// @Param request body codersdk.PutExtendWorkspaceRequest true "Extend deadline update request"
// @Success 200 {object} codersdk.Response
// @Router /workspaces/{workspace}/extend [put]
func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
workspace := httpmw.WorkspaceParam(r)
@@ -800,6 +881,14 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, code, resp)
}
// @Summary Watch workspace by ID
// @ID watch-workspace-id
// @Security CoderSessionToken
// @Produce text/event-stream
// @Tags Workspaces
// @Param workspace path string true "Workspace ID" format(uuid)
// @Success 200 {object} codersdk.Response
// @Router /workspaces/{workspace}/watch [get]
func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
workspace := httpmw.WorkspaceParam(r)
+5
View File
@@ -45,6 +45,7 @@ type DeploymentConfig struct {
Experimental *DeploymentConfigField[bool] `json:"experimental" typescript:",notnull"`
UpdateCheck *DeploymentConfigField[bool] `json:"update_check" typescript:",notnull"`
MaxTokenLifetime *DeploymentConfigField[time.Duration] `json:"max_token_lifetime" typescript:",notnull"`
Swagger *SwaggerConfig `json:"swagger" typescript:",notnull"`
}
type DERP struct {
@@ -145,6 +146,10 @@ type ProvisionerConfig struct {
ForceCancelInterval *DeploymentConfigField[time.Duration] `json:"force_cancel_interval" typescript:",notnull"`
}
type SwaggerConfig struct {
Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"`
}
type Flaggable interface {
string | time.Duration | bool | int | []string | []GitAuthConfig
}
+7 -7
View File
@@ -14,10 +14,10 @@ import (
// Template is the JSON representation of a Coder template. This type matches the
// database object for now, but is abstracted for ease of change later on.
type Template struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
OrganizationID uuid.UUID `json:"organization_id"`
ID uuid.UUID `json:"id" format:"uuid"`
CreatedAt time.Time `json:"created_at" format:"date-time"`
UpdatedAt time.Time `json:"updated_at" format:"date-time"`
OrganizationID uuid.UUID `json:"organization_id" format:"uuid"`
Name string `json:"name"`
DisplayName string `json:"display_name"`
Provisioner ProvisionerType `json:"provisioner"`
@@ -29,15 +29,15 @@ type Template struct {
Description string `json:"description"`
Icon string `json:"icon"`
DefaultTTLMillis int64 `json:"default_ttl_ms"`
CreatedByID uuid.UUID `json:"created_by_id"`
CreatedByID uuid.UUID `json:"created_by_id" format:"uuid"`
CreatedByName string `json:"created_by_name"`
AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs"`
}
type TransitionStats struct {
P50 *int64
P95 *int64
P50 *int64 `example:"123"`
P95 *int64 `example:"146"`
}
type TemplateBuildTimeStats map[WorkspaceTransition]TransitionStats
+13 -13
View File
@@ -51,32 +51,32 @@ const (
// WorkspaceBuild is an at-point representation of a workspace state.
// BuildNumbers start at 1 and increase by 1 for each subsequent build
type WorkspaceBuild struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
WorkspaceID uuid.UUID `json:"workspace_id"`
ID uuid.UUID `json:"id" format:"uuid"`
CreatedAt time.Time `json:"created_at" format:"date-time"`
UpdatedAt time.Time `json:"updated_at" format:"date-time"`
WorkspaceID uuid.UUID `json:"workspace_id" format:"uuid"`
WorkspaceName string `json:"workspace_name"`
WorkspaceOwnerID uuid.UUID `json:"workspace_owner_id"`
WorkspaceOwnerID uuid.UUID `json:"workspace_owner_id" format:"uuid"`
WorkspaceOwnerName string `json:"workspace_owner_name"`
TemplateVersionID uuid.UUID `json:"template_version_id"`
TemplateVersionID uuid.UUID `json:"template_version_id" format:"uuid"`
TemplateVersionName string `json:"template_version_name"`
BuildNumber int32 `json:"build_number"`
Transition WorkspaceTransition `json:"transition"`
InitiatorID uuid.UUID `json:"initiator_id"`
Transition WorkspaceTransition `json:"transition" enums:"start,stop,delete"`
InitiatorID uuid.UUID `json:"initiator_id" format:"uuid"`
InitiatorUsername string `json:"initiator_name"`
Job ProvisionerJob `json:"job"`
Reason BuildReason `db:"reason" json:"reason"`
Resources []WorkspaceResource `json:"resources"`
Deadline NullTime `json:"deadline,omitempty"`
Status WorkspaceStatus `json:"status"`
Status WorkspaceStatus `json:"status" enums:"pending,starting,running,stopping,stopped,failed,canceling,canceled,deleting,deleted"`
DailyCost int32 `json:"daily_cost"`
}
type WorkspaceResource struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"created_at"`
JobID uuid.UUID `json:"job_id"`
Transition WorkspaceTransition `json:"workspace_transition"`
ID uuid.UUID `json:"id" format:"uuid"`
CreatedAt time.Time `json:"created_at" format:"date-time"`
JobID uuid.UUID `json:"job_id" format:"uuid"`
Transition WorkspaceTransition `json:"workspace_transition" enums:"start,stop,delete"`
Type string `json:"type"`
Name string `json:"name"`
Hide bool `json:"hide"`
+6 -6
View File
@@ -17,12 +17,12 @@ import (
// Workspace is a deployment of a template. It references a specific
// version and can be updated.
type Workspace struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
OwnerID uuid.UUID `json:"owner_id"`
ID uuid.UUID `json:"id" format:"uuid"`
CreatedAt time.Time `json:"created_at" format:"date-time"`
UpdatedAt time.Time `json:"updated_at" format:"date-time"`
OwnerID uuid.UUID `json:"owner_id" format:"uuid"`
OwnerName string `json:"owner_name"`
TemplateID uuid.UUID `json:"template_id"`
TemplateID uuid.UUID `json:"template_id" format:"uuid"`
TemplateName string `json:"template_name"`
TemplateDisplayName string `json:"template_display_name"`
TemplateIcon string `json:"template_icon"`
@@ -32,7 +32,7 @@ type Workspace struct {
Name string `json:"name"`
AutostartSchedule *string `json:"autostart_schedule,omitempty"`
TTLMillis *int64 `json:"ttl_ms,omitempty"`
LastUsedAt time.Time `json:"last_used_at"`
LastUsedAt time.Time `json:"last_used_at" format:"date-time"`
}
type WorkspacesRequest struct {
+4
View File
@@ -0,0 +1,4 @@
# Authentication
- API Key (CoderSessionToken)
- Parameter Name: **Coder-Session-Token**, in: header.
+5
View File
@@ -0,0 +1,5 @@
Get started with Coder API:
<children>
This page is rendered on https://coder.com/docs/coder-oss/api. Refer to the other documents in the `api/` directory.
</children>
+1020
View File
File diff suppressed because it is too large Load Diff
+356
View File
@@ -0,0 +1,356 @@
# Templates
> This page is incomplete, stay tuned.
## Create template by organization
### Code samples
```shell
# Example request using curl
curl -X POST http://coder-server:8080/api/v2/organizations/{organization-id}/templates/ \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`POST /organizations/{organization-id}/templates/`
> Body parameter
```json
{
"allow_user_cancel_workspace_jobs": true,
"default_ttl_ms": 0,
"description": "string",
"display_name": "string",
"icon": "string",
"name": "string",
"parameter_values": [
{
"copy_from_parameter": "string",
"destination_scheme": "environment_variable",
"name": "string",
"source_scheme": "data",
"source_value": "string"
}
],
"template_version_id": "string"
}
```
### Parameters
| Name | In | Type | Required | Description |
| --------------- | ---- | -------------------------------------------------------------------------- | -------- | --------------- |
| organization-id | path | string | true | Organization ID |
| body | body | [codersdk.CreateTemplateRequest](schemas.md#codersdkcreatetemplaterequest) | true | Request body |
### Example responses
> 200 Response
```json
{
"active_user_count": 0,
"active_version_id": "string",
"allow_user_cancel_workspace_jobs": true,
"build_time_stats": {
"property1": {
"p50": 123,
"p95": 146
},
"property2": {
"p50": 123,
"p95": 146
}
},
"created_at": "2019-08-24T14:15:22Z",
"created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f",
"created_by_name": "string",
"default_ttl_ms": 0,
"description": "string",
"display_name": "string",
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"provisioner": "string",
"updated_at": "2019-08-24T14:15:22Z",
"workspace_owner_count": 0
}
```
### Responses
| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Template](schemas.md#codersdktemplate) |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
## Get templates by organization
### Code samples
```shell
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templates \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`GET /organizations/{organization}/templates`
### Parameters
| Name | In | Type | Required | Description |
| ------------ | ---- | ------------ | -------- | --------------- |
| organization | path | string(uuid) | true | Organization ID |
### Example responses
> 200 Response
```json
[
{
"active_user_count": 0,
"active_version_id": "string",
"allow_user_cancel_workspace_jobs": true,
"build_time_stats": {
"property1": {
"p50": 123,
"p95": 146
},
"property2": {
"p50": 123,
"p95": 146
}
},
"created_at": "2019-08-24T14:15:22Z",
"created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f",
"created_by_name": "string",
"default_ttl_ms": 0,
"description": "string",
"display_name": "string",
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"provisioner": "string",
"updated_at": "2019-08-24T14:15:22Z",
"workspace_owner_count": 0
}
]
```
### Responses
| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------- |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Template](schemas.md#codersdktemplate) |
<h3 id="get-templates-by-organization-responseschema">Response Schema</h3>
Status Code **200**
| Name | Type | Required | Restrictions | Description |
| ---------------------------------- | --------------------------------- | -------- | ------------ | ------------------------------------------ |
| _anonymous_ | array | false | none | none |
| » active_user_count | integer | false | none | ActiveUserCount is set to -1 when loading. |
| » active_version_id | string | false | none | none |
| » allow_user_cancel_workspace_jobs | boolean | false | none | none |
| » build_time_stats | `codersdk.TemplateBuildTimeStats` | false | none | none |
| »» **additionalProperties** | `codersdk.TransitionStats` | false | none | none |
| »»» p50 | integer | false | none | none |
| »»» p95 | integer | false | none | none |
| » created_at | string | false | none | none |
| » created_by_id | string | false | none | none |
| » created_by_name | string | false | none | none |
| » default_ttl_ms | integer | false | none | none |
| » description | string | false | none | none |
| » display_name | string | false | none | none |
| » icon | string | false | none | none |
| » id | string | false | none | none |
| » name | string | false | none | none |
| » organization_id | string | false | none | none |
| » provisioner | string | false | none | none |
| » updated_at | string | false | none | none |
| » workspace_owner_count | integer | false | none | none |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
## Get templates by organization and template name
### Code samples
```shell
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templates/{template-name} \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`GET /organizations/{organization}/templates/{template-name}`
### Parameters
| Name | In | Type | Required | Description |
| ------------- | ---- | ------------ | -------- | --------------- |
| organization | path | string(uuid) | true | Organization ID |
| template-name | path | string | true | Template name |
### Example responses
> 200 Response
```json
{
"active_user_count": 0,
"active_version_id": "string",
"allow_user_cancel_workspace_jobs": true,
"build_time_stats": {
"property1": {
"p50": 123,
"p95": 146
},
"property2": {
"p50": 123,
"p95": 146
}
},
"created_at": "2019-08-24T14:15:22Z",
"created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f",
"created_by_name": "string",
"default_ttl_ms": 0,
"description": "string",
"display_name": "string",
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"provisioner": "string",
"updated_at": "2019-08-24T14:15:22Z",
"workspace_owner_count": 0
}
```
### Responses
| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Template](schemas.md#codersdktemplate) |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
## Update template metadata by ID
### Code samples
```shell
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/templates/{id} \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`GET /templates/{id}`
### Parameters
| Name | In | Type | Required | Description |
| ---- | ---- | ------------ | -------- | ----------- |
| id | path | string(uuid) | true | Template ID |
### Example responses
> 200 Response
```json
{
"active_user_count": 0,
"active_version_id": "string",
"allow_user_cancel_workspace_jobs": true,
"build_time_stats": {
"property1": {
"p50": 123,
"p95": 146
},
"property2": {
"p50": 123,
"p95": 146
}
},
"created_at": "2019-08-24T14:15:22Z",
"created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f",
"created_by_name": "string",
"default_ttl_ms": 0,
"description": "string",
"display_name": "string",
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"provisioner": "string",
"updated_at": "2019-08-24T14:15:22Z",
"workspace_owner_count": 0
}
```
### Responses
| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Template](schemas.md#codersdktemplate) |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
## Delete template by ID
### Code samples
```shell
# Example request using curl
curl -X DELETE http://coder-server:8080/api/v2/templates/{id} \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`DELETE /templates/{id}`
### Parameters
| Name | In | Type | Required | Description |
| ---- | ---- | ------------ | -------- | ----------- |
| id | path | string(uuid) | true | Template ID |
### Example responses
> 200 Response
```json
{
"detail": "string",
"message": "string",
"validations": [
{
"detail": "string",
"field": "string"
}
]
}
```
### Responses
| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
+880
View File
@@ -0,0 +1,880 @@
# Workspaces
> This page is incomplete, stay tuned.
## Create workspace by organization
### Code samples
```shell
# Example request using curl
curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/members/{user}/workspaces \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`POST /organizations/{organization}/members/{user}/workspaces`
### Parameters
| Name | In | Type | Required | Description |
| ------------ | ---- | ------------ | -------- | --------------- |
| organization | path | string(uuid) | true | Organization ID |
| user | path | string | true | Username |
### Example responses
> 200 Response
```json
{
"autostart_schedule": "string",
"created_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"last_used_at": "2019-08-24T14:15:22Z",
"latest_build": {
"build_number": 0,
"created_at": "2019-08-24T14:15:22Z",
"daily_cost": 0,
"deadline": {
"time": "string",
"valid": true
},
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3",
"initiator_name": "string",
"job": {
"canceled_at": "string",
"completed_at": "string",
"created_at": "string",
"error": "string",
"file_id": "string",
"id": "string",
"started_at": "string",
"status": "string",
"tags": {
"property1": "string",
"property2": "string"
},
"worker_id": "string"
},
"reason": "string",
"resources": [
{
"agents": [
{
"apps": [
{
"command": "string",
"display_name": "string",
"external": true,
"health": "string",
"healthcheck": {
"interval": 0,
"threshold": 0,
"url": "string"
},
"icon": "string",
"id": "string",
"sharing_level": "string",
"slug": "string",
"subdomain": true,
"url": "string"
}
],
"architecture": "string",
"connection_timeout_seconds": 0,
"created_at": "string",
"directory": "string",
"disconnected_at": "string",
"environment_variables": {
"property1": "string",
"property2": "string"
},
"first_connected_at": "string",
"id": "string",
"instance_id": "string",
"last_connected_at": "string",
"latency": {
"property1": {
"latency_ms": 0,
"preferred": true
},
"property2": {
"latency_ms": 0,
"preferred": true
}
},
"name": "string",
"operating_system": "string",
"resource_id": "string",
"startup_script": "string",
"status": "string",
"troubleshooting_url": "string",
"updated_at": "string",
"version": "string"
}
],
"created_at": "2019-08-24T14:15:22Z",
"daily_cost": 0,
"hide": true,
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f",
"metadata": [
{
"key": "string",
"sensitive": true,
"value": "string"
}
],
"name": "string",
"type": "string",
"workspace_transition": "start"
}
],
"status": "pending",
"template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1",
"template_version_name": "string",
"transition": "start",
"updated_at": "2019-08-24T14:15:22Z",
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9",
"workspace_name": "string",
"workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7",
"workspace_owner_name": "string"
},
"name": "string",
"outdated": true,
"owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05",
"owner_name": "string",
"template_allow_user_cancel_workspace_jobs": true,
"template_display_name": "string",
"template_icon": "string",
"template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc",
"template_name": "string",
"ttl_ms": 0,
"updated_at": "2019-08-24T14:15:22Z"
}
```
### Responses
| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------- |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Workspace](schemas.md#codersdkworkspace) |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
## Get workspace metadata by owner and workspace name
### Code samples
```shell
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacename} \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`GET /users/{user}/workspace/{workspacename}`
### Parameters
| Name | In | Type | Required | Description |
| --------------- | ----- | ------- | -------- | ----------------------------------------------------------- |
| user | path | string | true | Owner username |
| workspacename | path | string | true | Workspace name |
| include_deleted | query | boolean | false | Return data instead of HTTP 404 if the workspace is deleted |
### Example responses
> 200 Response
```json
{
"autostart_schedule": "string",
"created_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"last_used_at": "2019-08-24T14:15:22Z",
"latest_build": {
"build_number": 0,
"created_at": "2019-08-24T14:15:22Z",
"daily_cost": 0,
"deadline": {
"time": "string",
"valid": true
},
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3",
"initiator_name": "string",
"job": {
"canceled_at": "string",
"completed_at": "string",
"created_at": "string",
"error": "string",
"file_id": "string",
"id": "string",
"started_at": "string",
"status": "string",
"tags": {
"property1": "string",
"property2": "string"
},
"worker_id": "string"
},
"reason": "string",
"resources": [
{
"agents": [
{
"apps": [
{
"command": "string",
"display_name": "string",
"external": true,
"health": "string",
"healthcheck": {
"interval": 0,
"threshold": 0,
"url": "string"
},
"icon": "string",
"id": "string",
"sharing_level": "string",
"slug": "string",
"subdomain": true,
"url": "string"
}
],
"architecture": "string",
"connection_timeout_seconds": 0,
"created_at": "string",
"directory": "string",
"disconnected_at": "string",
"environment_variables": {
"property1": "string",
"property2": "string"
},
"first_connected_at": "string",
"id": "string",
"instance_id": "string",
"last_connected_at": "string",
"latency": {
"property1": {
"latency_ms": 0,
"preferred": true
},
"property2": {
"latency_ms": 0,
"preferred": true
}
},
"name": "string",
"operating_system": "string",
"resource_id": "string",
"startup_script": "string",
"status": "string",
"troubleshooting_url": "string",
"updated_at": "string",
"version": "string"
}
],
"created_at": "2019-08-24T14:15:22Z",
"daily_cost": 0,
"hide": true,
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f",
"metadata": [
{
"key": "string",
"sensitive": true,
"value": "string"
}
],
"name": "string",
"type": "string",
"workspace_transition": "start"
}
],
"status": "pending",
"template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1",
"template_version_name": "string",
"transition": "start",
"updated_at": "2019-08-24T14:15:22Z",
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9",
"workspace_name": "string",
"workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7",
"workspace_owner_name": "string"
},
"name": "string",
"outdated": true,
"owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05",
"owner_name": "string",
"template_allow_user_cancel_workspace_jobs": true,
"template_display_name": "string",
"template_icon": "string",
"template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc",
"template_name": "string",
"ttl_ms": 0,
"updated_at": "2019-08-24T14:15:22Z"
}
```
### Responses
| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------- |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Workspace](schemas.md#codersdkworkspace) |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
## List workspaces
### Code samples
```shell
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/workspaces \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`GET /workspaces`
### Parameters
| Name | In | Type | Required | Description |
| --------- | ----- | ------ | -------- | ------------------------------------------- |
| owner | query | string | false | Filter by owner username |
| template | query | string | false | Filter by template name |
| name | query | string | false | Filter with partial-match by workspace name |
| status | query | string | false | Filter by workspace status |
| has_agent | query | string | false | Filter by agent status |
#### Enumerated Values
| Parameter | Value |
| --------- | ------------ |
| status | pending |
| status | running |
| status | stopping |
| status | stopped |
| status | failed |
| status | canceling |
| status | canceled |
| status | deleted |
| status | deleting |
| has_agent | connected |
| has_agent | connecting |
| has_agent | disconnected |
| has_agent | timeout |
### Example responses
> 200 Response
```json
{
"count": 0,
"workspaces": [
{
"autostart_schedule": "string",
"created_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"last_used_at": "2019-08-24T14:15:22Z",
"latest_build": {
"build_number": 0,
"created_at": "2019-08-24T14:15:22Z",
"daily_cost": 0,
"deadline": {
"time": "string",
"valid": true
},
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3",
"initiator_name": "string",
"job": {
"canceled_at": "string",
"completed_at": "string",
"created_at": "string",
"error": "string",
"file_id": "string",
"id": "string",
"started_at": "string",
"status": "string",
"tags": {
"property1": "string",
"property2": "string"
},
"worker_id": "string"
},
"reason": "string",
"resources": [
{
"agents": [
{
"apps": [
{
"command": "string",
"display_name": "string",
"external": true,
"health": "string",
"healthcheck": {},
"icon": "string",
"id": "string",
"sharing_level": "string",
"slug": "string",
"subdomain": true,
"url": "string"
}
],
"architecture": "string",
"connection_timeout_seconds": 0,
"created_at": "string",
"directory": "string",
"disconnected_at": "string",
"environment_variables": {
"property1": "string",
"property2": "string"
},
"first_connected_at": "string",
"id": "string",
"instance_id": "string",
"last_connected_at": "string",
"latency": {
"property1": {
"latency_ms": 0,
"preferred": true
},
"property2": {
"latency_ms": 0,
"preferred": true
}
},
"name": "string",
"operating_system": "string",
"resource_id": "string",
"startup_script": "string",
"status": "string",
"troubleshooting_url": "string",
"updated_at": "string",
"version": "string"
}
],
"created_at": "2019-08-24T14:15:22Z",
"daily_cost": 0,
"hide": true,
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f",
"metadata": [
{
"key": "string",
"sensitive": true,
"value": "string"
}
],
"name": "string",
"type": "string",
"workspace_transition": "start"
}
],
"status": "pending",
"template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1",
"template_version_name": "string",
"transition": "start",
"updated_at": "2019-08-24T14:15:22Z",
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9",
"workspace_name": "string",
"workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7",
"workspace_owner_name": "string"
},
"name": "string",
"outdated": true,
"owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05",
"owner_name": "string",
"template_allow_user_cancel_workspace_jobs": true,
"template_display_name": "string",
"template_icon": "string",
"template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc",
"template_name": "string",
"ttl_ms": 0,
"updated_at": "2019-08-24T14:15:22Z"
}
]
}
```
### Responses
| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------------------------- |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.WorkspacesResponse](schemas.md#codersdkworkspacesresponse) |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
## Get workspace metadata by ID
### Code samples
```shell
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/workspaces/{id} \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`GET /workspaces/{id}`
### Parameters
| Name | In | Type | Required | Description |
| --------------- | ----- | ------------ | -------- | ----------------------------------------------------------- |
| id | path | string(uuid) | true | Workspace ID |
| include_deleted | query | boolean | false | Return data instead of HTTP 404 if the workspace is deleted |
### Example responses
> 200 Response
```json
{
"autostart_schedule": "string",
"created_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"last_used_at": "2019-08-24T14:15:22Z",
"latest_build": {
"build_number": 0,
"created_at": "2019-08-24T14:15:22Z",
"daily_cost": 0,
"deadline": {
"time": "string",
"valid": true
},
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3",
"initiator_name": "string",
"job": {
"canceled_at": "string",
"completed_at": "string",
"created_at": "string",
"error": "string",
"file_id": "string",
"id": "string",
"started_at": "string",
"status": "string",
"tags": {
"property1": "string",
"property2": "string"
},
"worker_id": "string"
},
"reason": "string",
"resources": [
{
"agents": [
{
"apps": [
{
"command": "string",
"display_name": "string",
"external": true,
"health": "string",
"healthcheck": {
"interval": 0,
"threshold": 0,
"url": "string"
},
"icon": "string",
"id": "string",
"sharing_level": "string",
"slug": "string",
"subdomain": true,
"url": "string"
}
],
"architecture": "string",
"connection_timeout_seconds": 0,
"created_at": "string",
"directory": "string",
"disconnected_at": "string",
"environment_variables": {
"property1": "string",
"property2": "string"
},
"first_connected_at": "string",
"id": "string",
"instance_id": "string",
"last_connected_at": "string",
"latency": {
"property1": {
"latency_ms": 0,
"preferred": true
},
"property2": {
"latency_ms": 0,
"preferred": true
}
},
"name": "string",
"operating_system": "string",
"resource_id": "string",
"startup_script": "string",
"status": "string",
"troubleshooting_url": "string",
"updated_at": "string",
"version": "string"
}
],
"created_at": "2019-08-24T14:15:22Z",
"daily_cost": 0,
"hide": true,
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f",
"metadata": [
{
"key": "string",
"sensitive": true,
"value": "string"
}
],
"name": "string",
"type": "string",
"workspace_transition": "start"
}
],
"status": "pending",
"template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1",
"template_version_name": "string",
"transition": "start",
"updated_at": "2019-08-24T14:15:22Z",
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9",
"workspace_name": "string",
"workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7",
"workspace_owner_name": "string"
},
"name": "string",
"outdated": true,
"owner_id": "8826ee2e-7933-4665-aef2-2393f84a0d05",
"owner_name": "string",
"template_allow_user_cancel_workspace_jobs": true,
"template_display_name": "string",
"template_icon": "string",
"template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc",
"template_name": "string",
"ttl_ms": 0,
"updated_at": "2019-08-24T14:15:22Z"
}
```
### Responses
| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------- |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Workspace](schemas.md#codersdkworkspace) |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
## Update workspace metadata by ID
### Code samples
```shell
# Example request using curl
curl -X PATCH http://coder-server:8080/api/v2/workspaces/{workspace} \
-H 'Content-Type: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`PATCH /workspaces/{workspace}`
> Body parameter
```json
{
"name": "string"
}
```
### Parameters
| Name | In | Type | Required | Description |
| --------- | ---- | ---------------------------------------------------------------------------- | -------- | ----------------------- |
| workspace | path | string(uuid) | true | Workspace ID |
| body | body | [codersdk.UpdateWorkspaceRequest](schemas.md#codersdkupdateworkspacerequest) | true | Metadata update request |
### Responses
| Status | Meaning | Description | Schema |
| ------ | --------------------------------------------------------------- | ----------- | --------- |
| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | no schema |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
## Update workspace autostart schedule by ID
### Code samples
```shell
# Example request using curl
curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/autostart \
-H 'Content-Type: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`PUT /workspaces/{workspace}/autostart`
> Body parameter
```json
{
"schedule": "string"
}
```
### Parameters
| Name | In | Type | Required | Description |
| --------- | ---- | ---------------------------------------------------------------------------------------------- | -------- | ----------------------- |
| workspace | path | string(uuid) | true | Workspace ID |
| body | body | [codersdk.UpdateWorkspaceAutostartRequest](schemas.md#codersdkupdateworkspaceautostartrequest) | true | Schedule update request |
### Responses
| Status | Meaning | Description | Schema |
| ------ | --------------------------------------------------------------- | ----------- | --------- |
| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | no schema |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
## Extend workspace deadline by ID
### Code samples
```shell
# Example request using curl
curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/extend \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`PUT /workspaces/{workspace}/extend`
> Body parameter
```json
{
"deadline": "string"
}
```
### Parameters
| Name | In | Type | Required | Description |
| --------- | ---- | ---------------------------------------------------------------------------------- | -------- | ------------------------------ |
| workspace | path | string(uuid) | true | Workspace ID |
| body | body | [codersdk.PutExtendWorkspaceRequest](schemas.md#codersdkputextendworkspacerequest) | true | Extend deadline update request |
### Example responses
> 200 Response
```json
{
"detail": "string",
"message": "string",
"validations": [
{
"detail": "string",
"field": "string"
}
]
}
```
### Responses
| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
## Update workspace TTL by ID
### Code samples
```shell
# Example request using curl
curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/ttl \
-H 'Content-Type: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`PUT /workspaces/{workspace}/ttl`
> Body parameter
```json
{
"ttl_ms": 0
}
```
### Parameters
| Name | In | Type | Required | Description |
| --------- | ---- | ---------------------------------------------------------------------------------- | -------- | ---------------------------- |
| workspace | path | string(uuid) | true | Workspace ID |
| body | body | [codersdk.UpdateWorkspaceTTLRequest](schemas.md#codersdkupdateworkspacettlrequest) | true | Workspace TTL update request |
### Responses
| Status | Meaning | Description | Schema |
| ------ | --------------------------------------------------------------- | ----------- | --------- |
| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | no schema |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
## Watch workspace by ID
### Code samples
```shell
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/watch \
-H 'Accept: text/event-stream' \
-H 'Coder-Session-Token: API_KEY'
```
`GET /workspaces/{workspace}/watch`
### Parameters
| Name | In | Type | Required | Description |
| --------- | ---- | ------------ | -------- | ------------ |
| workspace | path | string(uuid) | true | Workspace ID |
### Example responses
> 200 Response
### Responses
| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) |
To perform this operation, you must be authenticated by means of one of the following methods: **CoderSessionToken**.
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20"><path d="m10 12-2-2 2-2 2 2ZM8.208 6.792l-2-2L10 1l3.792 3.792-2 2L10 5Zm-3.416 7L1 10l3.792-3.792 2 2L5 10l1.792 1.792Zm10.416 0-2-2L15 10l-1.792-1.792 2-2L19 10ZM10 19l-3.792-3.792 2-2L10 15l1.792-1.792 2 2Z"/></svg>

After

Width:  |  Height:  |  Size: 281 B

+68 -45
View File
@@ -10,16 +10,16 @@
{
"title": "Architecture",
"description": "Learn how Coder works",
"icon_path": "./images/icons/protractor.svg",
"path": "./about/architecture.md"
"path": "./about/architecture.md",
"icon_path": "./images/icons/protractor.svg"
}
]
},
{
"title": "Installation",
"description": "How to install and deploy Coder",
"icon_path": "./images/icons/download.svg",
"path": "./install/index.md",
"icon_path": "./images/icons/download.svg",
"children": [
{
"title": "Install script",
@@ -66,32 +66,32 @@
{
"title": "Quickstart",
"description": "Create your first template and workspace",
"icon_path": "./images/icons/star.svg",
"path": "./quickstart.md",
"icon_path": "./images/icons/star.svg",
"children": [
{
"title": "Docker",
"description": "Setup Coder with Docker",
"icon_path": "./images/icons/docker.svg",
"path": "./quickstart/docker.md"
"path": "./quickstart/docker.md",
"icon_path": "./images/icons/docker.svg"
},
{
"title": "Google Cloud Platform",
"description": "Setup Coder on a GCP Compute Engine VM",
"icon_path": "./images/google-cloud.svg",
"path": "./quickstart/google-cloud-platform.md"
"path": "./quickstart/google-cloud-platform.md",
"icon_path": "./images/google-cloud.svg"
},
{
"title": "AWS",
"description": "Setup Coder on an AWS EC2 VM",
"icon_path": "./images/aws.svg",
"path": "./quickstart/aws.md"
"path": "./quickstart/aws.md",
"icon_path": "./images/aws.svg"
},
{
"title": "Azure",
"description": "Setup Coder on an Azure VM",
"icon_path": "./images/azure.svg",
"path": "./quickstart/azure.md"
"path": "./quickstart/azure.md",
"icon_path": "./images/azure.svg"
}
]
},
@@ -105,8 +105,7 @@
"title": "Resource Persistence",
"description": "Learn how resource persistence works in Coder",
"path": "./templates/resource-persistence.md",
"icon_path": "./images/icons/infinity.svg",
"last_updated": "2022-10-23"
"icon_path": "./images/icons/infinity.svg"
},
{
"title": "Provider Authentication",
@@ -137,8 +136,8 @@
{
"title": "Workspaces",
"description": "Learn about Coder workspaces.",
"icon_path": "./images/icons/layers.svg",
"path": "./workspaces.md"
"path": "./workspaces.md",
"icon_path": "./images/icons/layers.svg"
},
{
"title": "IDEs",
@@ -184,45 +183,45 @@
{
"title": "Dotfiles",
"description": "Learn how to personalize your workspace",
"icon_path": "./images/icons/art-pad.svg",
"path": "./dotfiles.md"
"path": "./dotfiles.md",
"icon_path": "./images/icons/art-pad.svg"
},
{
"title": "Secrets",
"description": "Learn how to use secrets in your worskpace",
"icon_path": "./images/icons/secrets.svg",
"path": "./secrets.md"
"description": "Learn how to use secrets in your workspace",
"path": "./secrets.md",
"icon_path": "./images/icons/secrets.svg"
},
{
"title": "Administration",
"description": "How to install and deploy Coder",
"icon_path": "./images/icons/wrench.svg",
"path": "./admin/index.md",
"icon_path": "./images/icons/wrench.svg",
"children": [
{
"title": "Authentication",
"description": "Learn how to set up authentication using GitHub or OpenID Connect",
"icon_path": "./images/icons/key.svg",
"path": "./admin/auth.md"
"path": "./admin/auth.md",
"icon_path": "./images/icons/key.svg"
},
{
"title": "Users",
"description": "Learn about user roles available in Coder and how to create and manage users",
"icon_path": "./images/icons/users.svg",
"path": "./admin/users.md"
"path": "./admin/users.md",
"icon_path": "./images/icons/users.svg"
},
{
"title": "Groups",
"description": "Learn how to manage user groups",
"icon_path": "./images/icons/group.svg",
"path": "./admin/groups.md",
"icon_path": "./images/icons/group.svg",
"state": "enterprise"
},
{
"title": "RBAC",
"description": "Learn how to use the role based access control",
"icon_path": "./images/icons/rbac.svg",
"path": "./admin/rbac.md",
"icon_path": "./images/icons/rbac.svg",
"state": "enterprise"
},
{
@@ -234,74 +233,74 @@
{
"title": "Git Providers",
"description": "Learn how connect Coder with external git providers",
"icon_path": "./images/icons/git.svg",
"path": "./admin/git-providers.md"
"path": "./admin/git-providers.md",
"icon_path": "./images/icons/git.svg"
},
{
"title": "Upgrading",
"description": "Learn how to upgrade Coder",
"icon_path": "./images/icons/upgrade.svg",
"path": "./admin/upgrade.md"
"path": "./admin/upgrade.md",
"icon_path": "./images/icons/upgrade.svg"
},
{
"title": "Automation",
"description": "Learn how to automate Coder with the CLI and API",
"icon_path": "./images/icons/plug.svg",
"path": "./admin/automation.md"
"path": "./admin/automation.md",
"icon_path": "./images/icons/plug.svg"
},
{
"title": "Audit Logs",
"description": "Learn how to use Audit Logs in your Coder deployment",
"icon_path": "./images/icons/radar.svg",
"path": "./admin/audit-logs.md",
"icon_path": "./images/icons/radar.svg",
"state": "enterprise"
},
{
"title": "Quotas",
"description": "Learn how to use Workspace Quotas in Coder",
"icon_path": "./images/icons/dollar.svg",
"path": "./admin/quotas.md",
"icon_path": "./images/icons/dollar.svg",
"state": "enterprise"
},
{
"title": "High Availability",
"description": "Learn how to configure Coder for High Availability",
"icon_path": "./images/icons/hydra.svg",
"path": "./admin/high-availability.md",
"icon_path": "./images/icons/hydra.svg",
"state": "enterprise"
},
{
"title": "Prometheus",
"description": "Learn how to collect Prometheus metrics",
"icon_path": "./images/icons/speed.svg",
"path": "./admin/prometheus.md"
"path": "./admin/prometheus.md",
"icon_path": "./images/icons/speed.svg"
},
{
"title": "Service Banners",
"description": "Learn how to configure Service Banners",
"icon_path": "./images/icons/info.svg",
"path": "./admin/service-banners.md",
"icon_path": "./images/icons/info.svg",
"state": "enterprise"
},
{
"title": "Telemetry",
"description": "Learn what usage telemetry Coder collects",
"icon_path": "./images/icons/science.svg",
"path": "./admin/telemetry.md"
"path": "./admin/telemetry.md",
"icon_path": "./images/icons/science.svg"
}
]
},
{
"title": "Enterprise",
"description": "Learn how to enable Enterprise features",
"icon_path": "./images/icons/group.svg",
"path": "./enterprise.md"
"path": "./enterprise.md",
"icon_path": "./images/icons/group.svg"
},
{
"title": "Contributing",
"description": "Learn how to contribute to Coder",
"icon_path": "./images/icons/contributing.svg",
"path": "./CONTRIBUTING.md",
"icon_path": "./images/icons/contributing.svg",
"children": [
{
"title": "Code of Conduct",
@@ -319,6 +318,30 @@
"path": "./contributing/SECURITY.md"
}
]
},
{
"title": "API",
"description": "Learn how to use Coderd API",
"path": "./api/index.md",
"icon_path": "./images/icons/api.svg",
"children": [
{
"title": "Authentication",
"path": "./api/authentication.md"
},
{
"title": "Templates",
"path": "./api/templates.md"
},
{
"title": "Workspaces",
"path": "./api/workspaces.md"
},
{
"title": "Schemas",
"path": "./api/schemas.md"
}
]
}
]
}
+10
View File
@@ -125,6 +125,8 @@ require (
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.14.0
github.com/stretchr/testify v1.8.1
github.com/swaggo/http-swagger v1.3.3
github.com/swaggo/swag v1.8.6
github.com/tabbed/pqtype v0.1.1
github.com/u-root/u-root v0.10.0
github.com/unrolled/secure v1.13.0
@@ -169,6 +171,7 @@ require (
cloud.google.com/go/compute v1.12.1 // indirect
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
@@ -203,6 +206,10 @@ require (
github.com/gin-gonic/gin v1.7.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/spec v0.20.6 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect
@@ -226,6 +233,7 @@ require (
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/insomniacslk/dhcp v0.0.0-20211209223715-7d93572ebe8e // indirect
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/josharian/native v1.0.0 // indirect
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
@@ -234,6 +242,7 @@ require (
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
@@ -268,6 +277,7 @@ require (
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d // indirect
github.com/tailscale/golang-x-crypto v0.0.0-20221102133106-bc99ab8c2d17 // indirect
+17
View File
@@ -114,6 +114,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
@@ -674,17 +676,24 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ=
github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
@@ -1121,6 +1130,7 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
@@ -1246,6 +1256,7 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
@@ -1722,6 +1733,12 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/swaggest/assertjson v1.7.0 h1:SKw5Rn0LQs6UvmGrIdaKQbMR1R3ncXm5KNon+QJ7jtw=
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe h1:K8pHPVoTgxFJt1lXuIzzOX7zZhZFldJQK/CgKx9BFIc=
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
github.com/swaggo/http-swagger v1.3.3 h1:Hu5Z0L9ssyBLofaama21iYaF2VbWyA8jdohaaCGpHsc=
github.com/swaggo/http-swagger v1.3.3/go.mod h1:sE+4PjD89IxMPm77FnkDz0sdO+p5lbXzrVWT6OTVVGo=
github.com/swaggo/swag v1.8.6 h1:2rgOaLbonWu1PLP6G+/rYjSvPg0jQE0HtrEKuE380eg=
github.com/swaggo/swag v1.8.6/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg=
github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# This script generates swagger description file and required Go docs files
# from the coderd API.
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "$(dirname "${BASH_SOURCE[0]}")")/lib.sh"
APIDOCGEN_DIR=$(dirname "${BASH_SOURCE[0]}")
API_MD_TMP_FILE=$(mktemp /tmp/coder-apidocgen.XXXXXX)
cleanup() {
rm -f "${API_MD_TMP_FILE}"
}
trap cleanup EXIT
log "Use temporary file: ${API_MD_TMP_FILE}"
pushd "${PROJECT_ROOT}"
go run github.com/swaggo/swag/cmd/swag@v1.8.6 init \
--generalInfo="coderd.go" \
--dir="./coderd,./codersdk" \
--output="./coderd/apidoc" \
--outputTypes="go,json" \
--parseDependency=true
popd
pushd "${APIDOCGEN_DIR}"
npm ci
# Make sure that widdershins is installed correctly.
npm exec -- widdershins --version
# Render the Markdown file.
npm exec -- widdershins \
--user_templates "./markdown-template" \
--search false \
--omitHeader true \
--language_tabs "shell:curl" \
--summary "../../coderd/apidoc/swagger.json" \
--outfile "${API_MD_TMP_FILE}"
# Perform the postprocessing
go run postprocess/main.go -in-md-file-single "${API_MD_TMP_FILE}"
popd
@@ -0,0 +1,64 @@
## Swagger / OpenAPI 2 and OpenAPI 3 template parameters
Note that properties of OpenAPI objects will be in OpenAPI 3.0 form, as
Swagger / OpenAPI 2.0 definitions are converted automatically.
### Code templates
* `method` - the HTTP method of the operation (in lower-case)
* `methodUpper` - the HTTP method of the operation (in upper-case)
* `url` - the full URL of the operation (including protocol and host)
* `consumes[]` - an array of MIME-types the operation consumes
* `produces[]` - an array of MIME-types the operation produces
* `operation` - the current operation object
* `operationId` - the current operation id
* `opName` - the operationId if set, otherwise the method + path
* `tags[]` - the full list of tags applying to the operation
* `security` - the security definitions applying to the operation
* `resource` - the current tag/path object
* `parameters[]` - an array of parameters for the operation (see below)
* `queryString` - an example queryString, urlEncoded
* `requiredQueryString` - an example queryString for `required:true` parameters
* `queryParameters[]` - a subset of `parameters` that are `in:query`
* `requiredParameters[]` - a subset of `queryParameters` that are `required:true`
* `headerParameters[]` - a subset of `parameters` that are `in:header`
* `allHeaders[]` - a concatenation of `headerParameters` and pseudo-parameters `Accept` and `Content-Type`, and optionally `Authorization` (the latter has an `isAuth` boolean property set true so it can be omitted in templates if desired
### Parameter template
* `parameters[]` - an array of [parameters](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#parameterObject), including the following pseudo-properties
* `shortDesc` - a truncated version of the parameter description
* `safeType` - a computed version of the parameter type, including Body and schema names
* `originalType` - the original type of the parameter
* `exampleValues` - an object containing examples for use in code-templates
* `json` - example values in JSON compatible syntax
* `object` - example values in raw object form (unquoted strings etc)
* `depth` - a zero-based indicator of the depth of expanded request body parameters
* `enums[]` - an array of (parameter)name/value pairs
### Responses template
* `responses[]` - an array of [responses](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#responseObject), including `status` and `meaning` properties
### Authentication template
* `authenticationStr` - a simple string of methods (and scopes where appropriate)
* `securityDefinitions[]` - an array of applicable [securityDefinitions](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#securityRequirementObject)
### Schema Property template
* `schemaProperties[]` - an array of
* `name`
* `type`
* `required`
* `description`
* `enums[]` - an array of (schema property)name/value pairs
### Common to all templates
* `openapi` - the top-level OpenAPI / Swagger document
* `header` - the front-matter of the Slate/Shins markdown document
* `host` - the (computed) host of the API
* `protocol` - the default/first protocol of the API
* `baseUrl` - the (computed) baseUrl of the API (including protocol and host)
* `widdershins` - the contents of widdershins `package.json`
@@ -0,0 +1 @@
To perform this operation, you must be authenticated by means of one of the following methods: **{{= data.utils.getAuthenticationStr(data) }}**.
@@ -0,0 +1,4 @@
# Example request using curl
curl -X {{=data.methodUpper}} http://coder-server:8080{{=data.url}}{{=data.requiredQueryString}}{{?data.allHeaders.length}} \{{?}}
{{~data.allHeaders :p:index}} -H '{{=p.name}}: {{=p.exampleValues.object}}'{{?index < data.allHeaders.length-1}} \{{?}}
{{~}}
@@ -0,0 +1 @@
{{= data.utils.inspect(data) }}
@@ -0,0 +1,157 @@
{{
function renderSinglePropertyType(p) {
if (!p.$ref) {
return p.type;
}
const pRef = p.$ref.replace("#/components/schemas/","");
if (pRef == "codersdk.NullTime") {
return "string(time) or `null`";
}
return "[" + pRef + "](#" + pRef.replace(".","").toLowerCase() + ")";
}
function renderPropertyType(p) {
if (p.type == "array") {
return "array of " + renderSinglePropertyType(p.schema.items);
}
return renderSinglePropertyType(p);
}
function renderDisplayName(p) {
if (p.displayName == "» **additionalProperties**") {
return "» `[any property]`";
}
if (p.displayName == "**additionalProperties**") {
return "`[any property]`";
}
return "`" + p.displayName + "`";
}
function renderDescription(p) {
if (!p.description) {
return "none";
}
const toSnakeCase = str =>
str
.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
.map(x => x.toLowerCase())
.join('_');
const words = p.description.split(' ');
if (words.length == 0) {
return "none";
}
const countUppercase = words[0].length - words[0].replace(/[A-Z]/g, '').length;
if (countUppercase > 1) {
const displayName = p.displayName.charAt(0).toUpperCase() + p.displayName.replaceAll("_", " ").toLowerCase().slice(1);
return displayName + " " + words.slice(1).join(' ');
}
return p.description;
}
}}
{{? data.api.components && data.api.components.securitySchemes }}{{#def.security}}{{?}}
{{ for (var r in data.resources) { }}
{{ data.resource = data.resources[r]; }}
<!-- APIDOCGEN: BEGIN SECTION -->
{{= data.tags.section }}# {{= r}}
> This page is incomplete, stay tuned.
{{? data.resource.description }}{{= data.resource.description}}{{?}}
{{ for (var m in data.resource.methods) { }}
{{ data.operationUniqueName = m; }}
{{ data.method = data.resource.methods[m]; }}
{{ data.operationUniqueSlug = data.method.slug; }}
{{ data.operation = data.method.operation; }}
{{= data.templates.operation(data) }}
{{ } /* of methods */ }}
{{= data.tags.endSection }}
{{ } /* of resources */ }}
{{? data.api.components && data.api.components.schemas }}
{{= data.tags.section }}
<!-- APIDOCGEN: BEGIN SECTION -->
# Schemas
> This page is incomplete, stay tuned.
{{ for (var s in data.components.schemas) {
if (s == "codersdk.NullTime") {
continue;
}
}}
{{ var origSchema = data.components.schemas[s]; }}
{{ var schema = data.api.components.schemas[s]; }}
{{= data.tags.section }}
## {{=s}}
{{? data.options.yaml }}
```yaml
{{=data.utils.yaml.stringify(data.utils.getSample(schema,data.options,{quiet:true},data.api))}}
{{??}}
```json
{{=data.utils.safejson(data.utils.getSample(schema,data.options,{quiet:true},data.api),null,2)}}
{{?}}```
{{ var enums = []; }}
{{ var blocks = data.utils.schemaToArray(origSchema,-1,{trim:true,join:true},data); }}
{{ for (var block of blocks) {
for (var p of block.rows) {
if (p.schema && p.schema.enum) {
for (var e of p.schema.enum) {
enums.push({name:p.name,value:e});
}
}
}
}
}}
{{~ blocks :block}}
{{? block.title }}{{= block.title}}{{= '\n\n'}}{{?}}
{{? block.externalDocs}}
<a href="{{=block.externalDocs.url}}">{{=block.externalDocs.description||'External documentation'}}</a>
{{?}}
{{? block===blocks[0] }}
{{= data.tags.section }}
### Properties
{{?}}
{{? block.rows.length}}|Name|Type|Required|Restrictions|Description|
|---|---|---|---|---|{{?}}
{{~ block.rows :p}}|{{= renderDisplayName(p)}}|{{= renderPropertyType(p)}}|{{=p.required}}|{{=p.restrictions||'none'}}|{{= renderDescription(p)}}|
{{~}}
{{~}}
{{? (blocks[0].rows.length === 0) && (blocks.length === 1) }}
*None*
{{?}}
{{? enums.length > 0 }}
{{= data.tags.section }}
#### Enumerated Values
|Property|Value|
|---|---|
{{~ enums :e}}|{{=e.name}}|{{=data.utils.toPrimitive(e.value)}}|
{{~}}
{{= data.tags.endSection }}
{{?}}
{{= data.tags.endSection }}
{{= data.tags.endSection }}
{{ } /* of schemas */ }}
{{?}}
@@ -0,0 +1,48 @@
{{= data.tags.section }}
## {{= data.operationUniqueName}}
{{ data.methodUpper = data.method.verb.toUpperCase(); }}
{{ data.url = data.utils.slashes(data.baseUrl + data.method.path); }}
{{ data.parameters = data.operation.parameters; }}
{{ data.enums = []; }}
{{ data.utils.fakeProdCons(data); }}
{{ data.utils.fakeBodyParameter(data); }}
{{ data.utils.mergePathParameters(data); }}
{{ data.utils.getParameters(data); }}
{{? data.options.codeSamples || data.operation["x-code-samples"] }}
### Code samples
{{= data.utils.getCodeSamples(data)}}
{{?}}
`{{= data.methodUpper}} {{=data.method.path}}`
{{? data.operation.summary && !data.options.tocSummary}}*{{= data.operation.summary }}*{{?}}
{{? data.operation.description}}{{= data.operation.description }}{{?}}
{{? data.operation.requestBody}}
> Body parameter
{{? data.bodyParameter.exampleValues.description }}
> {{= data.bodyParameter.exampleValues.description }}
{{?}}
{{= data.utils.getBodyParameterExamples(data) }}
{{?}}
{{? data.parameters && data.parameters.length }}
{{#def.parameters}}
{{?}}
{{#def.responses}}
{{ data.security = data.operation.security ? data.operation.security : data.api.security; }}
{{? data.security && data.security.length }}
{{#def.authentication}}
{{??}}
{{#def.authentication_none}}
{{?}}
{{= data.tags.endSection }}
@@ -0,0 +1,50 @@
{{
function renderParameterType(p) {
if (p.schema['x-widdershins-oldRef']) {
const aType = p.schema['x-widdershins-oldRef'].replace("#/components/schemas/","");
const href = aType.replace(".","").toLowerCase();
return "[" + aType + "](schemas.md#" + href + ")";
}
return p.safeType;
}
}}
{{= data.tags.section }}
### Parameters
|Name|In|Type|Required|Description|
|---|---|---|---|---|
{{~ data.parameters :p}}|{{=p.name}}|{{=p.in}}|{{= renderParameterType(p)}}|{{=p.required}}|{{=p.shortDesc || 'none'}}|
{{~}}
{{? data.longDescs }}
#### Detailed descriptions
{{~ data.parameters :p}}{{? p.shortDesc !== p.description}}
**{{=p.name}}**: {{=p.description}}{{?}}
{{~}}
{{?}}
{{~ data.parameters :p}}
{{? p.schema && p.schema.enum }}
{{~ p.schema.enum :e}}
{{ var entry = {}; entry.name = p.name; entry.value = e; data.enums.push(entry); }}
{{~}}
{{?}}
{{? p.schema && p.schema.items && p.schema.items.enum }}
{{~ p.schema.items.enum :e}}
{{ var entry = {}; entry.name = p.name; entry.value = e; data.enums.push(entry); }}
{{~}}
{{?}}
{{~}}
{{? data.enums && data.enums.length }}
#### Enumerated Values
|Parameter|Value|
|---|---|
{{~ data.enums :e}}|{{=e.name}}|{{=data.utils.toPrimitive(e.value)}}|
{{~}}
{{?}}
{{= data.tags.endSection }}
@@ -0,0 +1,113 @@
{{
function renderSingleResponseType(r) {
var content;
for (var ct in r.content) {
content = r.content[ct];
break;
}
if (!content) {
return "no schema";
}
var ref = content.schema["x-widdershins-oldRef"];
if (!ref) {
ref = content.schema.items["x-widdershins-oldRef"];
}
const aType = ref.replace("#/components/schemas/","");
const href = aType.replace(".","").toLowerCase();
return "[" + aType + "](schemas.md#" + href + ")";
}
function renderResponseType(r) {
if (r.type == "array") {
return "array of " + renderSingleResponseType(r);
}
return renderSingleResponseType(r);
}
}}
{{ data.responses = data.utils.getResponses(data); }}
{{ data.responseSchemas = false; }}
{{~ data.responses :response }}
{{ if (response.content) data.responseSchemas = true; }}
{{~}}
{{? data.responseSchemas }}
### Example responses
{{= data.utils.getResponseExamples(data) }}
{{?}}
{{= data.tags.section }}
### Responses
|Status|Meaning|Description|Schema|
|---|---|---|---|
{{~ data.responses :r}}|{{=r.status}}|{{=r.meaning}}|{{=r.description || 'none'}}|{{= renderResponseType(r)}}|
{{~}}
{{ data.responseSchemas = false; }}
{{~ data.responses :response }}
{{ if (response.content && !response.$ref && !data.utils.isPrimitive(response.type)) data.responseSchemas = true; }}
{{~}}
{{? data.responseSchemas }}
<h3 id="{{=data.operationUniqueSlug}}-responseschema">Response Schema</h3>
{{~ data.responses :response}}
{{? response.content && !response.$ref && !data.utils.isPrimitive(response.type)}}
{{? Object.keys(response.content).length }}
{{ var responseKey = Object.keys(response.content)[0]; }}
{{ var responseSchema = response.content[responseKey].schema; }}
{{ var enums = []; }}
{{ var blocks = data.utils.schemaToArray(responseSchema,0,{trim:true,join:true},data); }}
{{ for (var block of blocks) {
for (var p of block.rows) {
if (p.schema && p.schema.enum) {
for (var e of p.schema.enum) {
enums.push({name:p.name,value:e});
}
}
}
}
}}
{{? blocks[0].rows.length || blocks[0].title }}
Status Code **{{=response.status}}**
{{~ blocks :block}}
{{? block.title }}*{{=block.title}}*
{{?}}
|Name|Type|Required|Restrictions|Description|
|---|---|---|---|---|
{{~block.rows :p}}|{{=p.displayName}}|{{? p.$ref}}`{{=p.$ref}}`{{?}}{{? !p.$ref}}{{=p.type}}{{?}}|{{=p.required}}|{{=p.restrictions||'none'}}|{{=p.description||'none'}}|
{{~}}
{{~}}
{{?}}
{{? enums.length > 0 }}
#### Enumerated Values
|Property|Value|
|---|---|
{{~ enums :e}}|{{=e.name}}|{{=data.utils.toPrimitive(e.value)}}|
{{~}}
{{?}}
{{?}}
{{ data.response = response; }}
{{?}}
{{~}}
{{?}}
{{ data.responseHeaders = data.utils.getResponseHeaders(data); }}
{{? data.responseHeaders.length }}
### Response Headers
|Status|Header|Type|Format|Description|
|---|---|---|---|---|
{{~ data.responseHeaders :h}}|{{=h.status}}|{{=h.header}}|{{=h.type}}|{{=h.format||''}}|{{=h.description||'none'}}|
{{~}}
{{?}}
{{= data.tags.endSection }}
@@ -0,0 +1,27 @@
<!-- APIDOCGEN: BEGIN SECTION -->
{{= data.tags.section }}# Authentication
{{ for (var s in data.api.components.securitySchemes) { }}
{{ var sd = data.api.components.securitySchemes[s]; }}
{{? sd.type == 'apiKey' }}
- API Key ({{=s}})
- Parameter Name: **{{=sd.name}}**, in: {{=sd.in}}. {{=sd.description || ''}}
{{?}}
{{? sd.type == 'http'}}
- HTTP Authentication, scheme: {{=sd.scheme}}{{? sd.description }}<br/>{{=sd.description}}{{?}}
{{?}}
{{? sd.type == 'oauth2'}}
- oAuth2 authentication. {{=sd.description || ''}}
{{ for (var f in sd.flows) { }}
{{ var flow = sd.flows[f]; }}
- Flow: {{=f}}
{{? flow.authorizationUrl}} - Authorization URL = [{{=flow.authorizationUrl}}]({{=flow.authorizationUrl}}){{?}}
{{? flow.tokenUrl}} - Token URL = [{{=flow.tokenUrl}}]({{=flow.tokenUrl}}){{?}}
{{? flow.scopes && Object.keys(flow.scopes).length}}
|Scope|Scope Description|
|---|---|
{{ for (var sc in flow.scopes) { }}|{{=sc}}|{{=data.utils.join(flow.scopes[sc])}}|
{{ } /* of scopes */ }}
{{?}}
{{ } /* of flows */ }}
{{?}}
{{ } /* of securitySchemes */ }}
+3369
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
{
"dependencies": {
"widdershins": "^4.0.1"
}
}
+206
View File
@@ -0,0 +1,206 @@
package main
import (
"bufio"
"bytes"
"encoding/json"
"flag"
"log"
"os"
"path"
"regexp"
"strings"
"golang.org/x/xerrors"
)
const (
apiSubdir = "api"
apiIndexFile = "index.md"
apiIndexContent = `Get started with Coder API:
<children>
This page is rendered on https://coder.com/docs/coder-oss/api. Refer to the other documents in the ` + "`" + `api/` + "`" + ` directory.
</children>
`
)
var (
docsDirectory string
inMdFileSingle string
sectionSeparator = []byte("<!-- APIDOCGEN: BEGIN SECTION -->\n")
nonAlphanumericRegex = regexp.MustCompile(`[^a-z0-9 ]+`)
)
func main() {
log.Println("Postprocess API docs")
flag.StringVar(&docsDirectory, "docs-directory", "../../docs", "Path to Coder docs directory")
flag.StringVar(&inMdFileSingle, "in-md-file-single", "", "Path to single Markdown file, output from widdershins.js")
flag.Parse()
if inMdFileSingle == "" {
flag.Usage()
log.Fatal("missing value for in-md-file-single")
}
sections, err := loadMarkdownSections()
if err != nil {
log.Fatal("can't load markdown sections: ", err)
}
err = prepareDocsDirectory()
if err != nil {
log.Fatal("can't prepare docs directory: ", err)
}
err = writeDocs(sections)
if err != nil {
log.Fatal("can't write docs directory: ", err)
}
log.Println("Done")
}
func loadMarkdownSections() ([][]byte, error) {
log.Printf("Read the md-file-single: %s", inMdFileSingle)
mdFile, err := os.ReadFile(inMdFileSingle)
if err != nil {
return nil, xerrors.Errorf("can't read the md-file-single: %w", err)
}
log.Printf("Read %dB", len(mdFile))
sections := bytes.Split(mdFile, sectionSeparator)
if len(sections) < 2 {
return nil, xerrors.Errorf("At least 1 section is expected: %w", err)
}
sections = sections[1:] // Skip the first element which is the empty byte array
log.Printf("Loaded %d sections", len(sections))
return sections, nil
}
func prepareDocsDirectory() error {
log.Println("Prepare docs directory")
apiPath := path.Join(docsDirectory, apiSubdir)
err := os.RemoveAll(apiPath)
if err != nil {
return xerrors.Errorf(`os.RemoveAll failed for "%s": %w`, apiPath, err)
}
err = os.MkdirAll(apiPath, 0755)
if err != nil {
return xerrors.Errorf(`os.MkdirAll failed for "%s": %w`, apiPath, err)
}
return nil
}
func writeDocs(sections [][]byte) error {
log.Println("Write docs to destination")
apiDir := path.Join(docsDirectory, apiSubdir)
err := os.WriteFile(path.Join(apiDir, apiIndexFile), []byte(apiIndexContent), 0644) // #nosec
if err != nil {
return xerrors.Errorf(`can't write the index file: %w`, err)
}
type mdFile struct {
title string
path string
}
var mdFiles []mdFile
// Write .md files for grouped API method (Templates, Workspaces, etc.)
for _, section := range sections {
sectionName, err := extractSectionName(section)
if err != nil {
return xerrors.Errorf("can't extract section name: %w", err)
}
log.Printf("Write section: %s", sectionName)
mdFilename := toMdFilename(sectionName)
docPath := path.Join(apiDir, mdFilename)
err = os.WriteFile(docPath, section, 0644) // #nosec
if err != nil {
return xerrors.Errorf(`can't write doc file "%s": %w`, docPath, err)
}
mdFiles = append(mdFiles, mdFile{
title: sectionName,
path: "./" + path.Join(apiSubdir, mdFilename),
})
}
// Update manifest.json
type route struct {
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Path string `json:"path,omitempty"`
IconPath string `json:"icon_path,omitempty"`
State string `json:"state,omitempty"`
Children []route `json:"children,omitempty"`
}
type manifest struct {
Versions []string `json:"versions,omitempty"`
Routes []route `json:"routes,omitempty"`
}
manifestPath := path.Join(docsDirectory, "manifest.json")
manifestFile, err := os.ReadFile(manifestPath)
if err != nil {
return xerrors.Errorf("can't read manifest file: %w", err)
}
log.Printf("Read manifest file: %dB", len(manifestFile))
var m manifest
err = json.Unmarshal(manifestFile, &m)
if err != nil {
return xerrors.Errorf("json.Unmarshal failed: %w", err)
}
for i, r := range m.Routes {
if r.Title != "API" {
continue
}
var children []route
for _, mdf := range mdFiles {
docRoute := route{
Title: mdf.title,
Path: mdf.path,
}
children = append(children, docRoute)
}
m.Routes[i].Children = children
break
}
manifestFile, err = json.MarshalIndent(m, "", " ")
if err != nil {
return xerrors.Errorf("json.Marshal failed: %w", err)
}
err = os.WriteFile(manifestPath, manifestFile, 0644) // #nosec
if err != nil {
return xerrors.Errorf("can't write manifest file: %w", err)
}
log.Printf("Write manifest file: %dB", len(manifestFile))
return nil
}
func extractSectionName(section []byte) (string, error) {
scanner := bufio.NewScanner(bytes.NewReader(section))
if !scanner.Scan() {
return "", xerrors.Errorf("section header was expected")
}
header := scanner.Text()[2:] // Skip #<space>
return strings.TrimSpace(header), nil
}
func toMdFilename(sectionName string) string {
return nonAlphanumericRegex.ReplaceAllLiteralString(strings.ToLower(sectionName), "-") + ".md"
}
+1 -1
View File
@@ -121,7 +121,7 @@ fatal() {
trap 'fatal "Script encountered an error"' ERR
cdroot
start_cmd API "" "${CODER_DEV_SHIM}" server --http-address 0.0.0.0:3000
start_cmd API "" "${CODER_DEV_SHIM}" server --http-address 0.0.0.0:3000 --swagger-enable
echo '== Waiting for Coder to become ready'
# Start the timeout in the background so interrupting this script
+1 -1
View File
@@ -26,7 +26,7 @@ var (
func main() {
flag.StringVar(&metricsFile, "metrics-file", "scripts/metricsdocgen/metrics", "Path to Prometheus metrics file")
flag.StringVar(&prometheusDocFile, "prometheus-doc-file", "docs/admin/prometheus.md", "Path to prometheus doc file")
flag.StringVar(&prometheusDocFile, "prometheus-doc-file", "docs/admin/prometheus.md", "Path to Prometheus doc file")
flag.BoolVar(&dryRun, "dry-run", false, "Dry run")
flag.Parse()
+6
View File
@@ -307,6 +307,7 @@ export interface DeploymentConfig {
readonly experimental: DeploymentConfigField<boolean>
readonly update_check: DeploymentConfigField<boolean>
readonly max_token_lifetime: DeploymentConfigField<number>
readonly swagger: SwaggerConfig
}
// From codersdk/deploymentconfig.go
@@ -616,6 +617,11 @@ export interface ServiceBanner {
readonly background_color?: string
}
// From codersdk/deploymentconfig.go
export interface SwaggerConfig {
readonly enable: DeploymentConfigField<boolean>
}
// From codersdk/deploymentconfig.go
export interface TLSConfig {
readonly enable: DeploymentConfigField<boolean>
+4
View File
@@ -25,6 +25,10 @@ export default defineConfig({
ws: true,
secure: process.env.NODE_ENV === "production",
},
"/swagger": {
target: process.env.CODER_HOST || "http://localhost:3000",
secure: process.env.NODE_ENV === "production",
},
},
},
resolve: {