Merge pull request #2553 from Omicron7/improve-rest-api-and-docs

chore: Improve API docs and fix REST issues.
This commit is contained in:
Denis Gukov 2024-11-26 01:13:09 +05:00 committed by GitHub
commit db8c24dbcb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 249 additions and 53 deletions

View File

@ -101,18 +101,25 @@ func resolveCapability(caps []string, resolved []string, uid string) {
repoID = pRepo.ID repoID = pRepo.ID
case "inventory": case "inventory":
res, err := store.CreateInventory(db.Inventory{ res, err := store.CreateInventory(db.Inventory{
ProjectID: userProject.ID, ProjectID: userProject.ID,
Name: "ITI-" + uid, Name: "ITI-" + uid,
Type: "static", Type: "static",
SSHKeyID: &userKey.ID, SSHKeyID: &userKey.ID,
BecomeKeyID: &userKey.ID, BecomeKeyID: &userKey.ID,
Inventory: "Test Inventory", Inventory: "Test Inventory",
RepositoryID: &repoID,
}) })
printError(err) printError(err)
inventoryID = res.ID inventoryID = res.ID
case "environment": case "environment":
pwd := "test-pass" pwd := "test-pass"
env := "{}" env := "{}"
secret := db.EnvironmentSecret{
Type: db.EnvironmentSecretEnv,
Name: "TEST",
Secret: "VALUE",
Operation: "create",
}
res, err := store.CreateEnvironment(db.Environment{ res, err := store.CreateEnvironment(db.Environment{
ProjectID: userProject.ID, ProjectID: userProject.ID,
Name: "ITI-" + uid, Name: "ITI-" + uid,
@ -121,6 +128,14 @@ func resolveCapability(caps []string, resolved []string, uid string) {
ENV: &env, ENV: &env,
}) })
printError(err) printError(err)
_, err = store.CreateAccessKey(db.AccessKey{
Name: string(secret.Type) + "." + secret.Name,
String: secret.Secret,
EnvironmentID: &res.ID,
ProjectID: &userProject.ID,
Type: db.AccessKeyString,
})
printError(err)
environmentID = res.ID environmentID = res.ID
case "template": case "template":
args := "[]" args := "[]"
@ -139,6 +154,7 @@ func resolveCapability(caps []string, resolved []string, uid string) {
ViewID: &view.ID, ViewID: &view.ID,
App: db.AppAnsible, App: db.AppAnsible,
GitBranch: &branch, GitBranch: &branch,
SurveyVars: []db.SurveyVar{},
}) })
printError(err) printError(err)
@ -218,6 +234,7 @@ func alterRequestBody(t *trans.Transaction) {
bodyFieldProcessor("inventory_id", inventoryID, &request) bodyFieldProcessor("inventory_id", inventoryID, &request)
bodyFieldProcessor("repository_id", repoID, &request) bodyFieldProcessor("repository_id", repoID, &request)
bodyFieldProcessor("template_id", templateID, &request) bodyFieldProcessor("template_id", templateID, &request)
bodyFieldProcessor("build_template_id", nil, &request)
if task != nil { if task != nil {
bodyFieldProcessor("task_id", task.ID, &request) bodyFieldProcessor("task_id", task.ID, &request)
} }

View File

@ -93,17 +93,22 @@ func main() {
h.Before("integration > /api/project/{project_id}/integrations/{integration_id}/matchers > Add Integration Matcher > 204 > application/json", capabilityWrapper("integration")) h.Before("integration > /api/project/{project_id}/integrations/{integration_id}/matchers > Add Integration Matcher > 204 > application/json", capabilityWrapper("integration"))
h.Before("integration > /api/project/{project_id}/integrations/{integration_id}/matchers/{matcher_id} > Updates Integration Matcher > 204 > application/json", capabilityWrapper("integrationmatcher")) h.Before("integration > /api/project/{project_id}/integrations/{integration_id}/matchers/{matcher_id} > Updates Integration Matcher > 204 > application/json", capabilityWrapper("integrationmatcher"))
h.Before("project > /api/project/{project_id}/keys > Add access key > 201 > application/json", capabilityWrapper("access_key"))
h.Before("project > /api/project/{project_id}/keys/{key_id} > Updates access key > 204 > application/json", capabilityWrapper("access_key")) h.Before("project > /api/project/{project_id}/keys/{key_id} > Updates access key > 204 > application/json", capabilityWrapper("access_key"))
h.Before("project > /api/project/{project_id}/keys/{key_id} > Removes access key > 204 > application/json", capabilityWrapper("access_key")) h.Before("project > /api/project/{project_id}/keys/{key_id} > Removes access key > 204 > application/json", capabilityWrapper("access_key"))
h.Before("project > /api/project/{project_id}/repositories > Add repository > 204 > application/json", capabilityWrapper("access_key")) h.Before("project > /api/project/{project_id}/repositories > Add repository > 201 > application/json", capabilityWrapper("access_key"))
h.Before("project > /api/project/{project_id}/repositories/{repository_id} > Get repository > 200 > application/json", capabilityWrapper("repository"))
h.Before("project > /api/project/{project_id}/repositories/{repository_id} > Updates repository > 204 > application/json", capabilityWrapper("repository")) h.Before("project > /api/project/{project_id}/repositories/{repository_id} > Updates repository > 204 > application/json", capabilityWrapper("repository"))
h.Before("project > /api/project/{project_id}/repositories/{repository_id} > Removes repository > 204 > application/json", capabilityWrapper("repository")) h.Before("project > /api/project/{project_id}/repositories/{repository_id} > Removes repository > 204 > application/json", capabilityWrapper("repository"))
h.Before("project > /api/project/{project_id}/inventory > create inventory > 201 > application/json", capabilityWrapper("inventory")) h.Before("project > /api/project/{project_id}/inventory > create inventory > 201 > application/json", capabilityWrapper("inventory"))
h.Before("project > /api/project/{project_id}/inventory/{inventory_id} > Get inventory > 200 > application/json", capabilityWrapper("inventory"))
h.Before("project > /api/project/{project_id}/inventory/{inventory_id} > Updates inventory > 204 > application/json", capabilityWrapper("inventory")) h.Before("project > /api/project/{project_id}/inventory/{inventory_id} > Updates inventory > 204 > application/json", capabilityWrapper("inventory"))
h.Before("project > /api/project/{project_id}/inventory/{inventory_id} > Removes inventory > 204 > application/json", capabilityWrapper("inventory")) h.Before("project > /api/project/{project_id}/inventory/{inventory_id} > Removes inventory > 204 > application/json", capabilityWrapper("inventory"))
h.Before("project > /api/project/{project_id}/environment > Add environment > 201 > application/json", capabilityWrapper("environment"))
h.Before("project > /api/project/{project_id}/environment/{environment_id} > Get environment > 200 > application/json", capabilityWrapper("environment"))
h.Before("project > /api/project/{project_id}/environment/{environment_id} > Update environment > 204 > application/json", capabilityWrapper("environment")) h.Before("project > /api/project/{project_id}/environment/{environment_id} > Update environment > 204 > application/json", capabilityWrapper("environment"))
h.Before("project > /api/project/{project_id}/environment/{environment_id} > Removes environment > 204 > application/json", capabilityWrapper("environment")) h.Before("project > /api/project/{project_id}/environment/{environment_id} > Removes environment > 204 > application/json", capabilityWrapper("environment"))

View File

@ -80,10 +80,15 @@ definitions:
type: string type: string
x-example: test@ansiblesemaphore.test x-example: test@ansiblesemaphore.test
example: test@ansiblesemaphore.test example: test@ansiblesemaphore.test
password:
type: string
format: password
alert: alert:
type: boolean type: boolean
admin: admin:
type: boolean type: boolean
external:
type: boolean
UserPutRequest: UserPutRequest:
type: object type: object
@ -104,6 +109,7 @@ definitions:
type: boolean type: boolean
admin: admin:
type: boolean type: boolean
User: User:
type: object type: object
properties: properties:
@ -122,6 +128,8 @@ definitions:
type: boolean type: boolean
admin: admin:
type: boolean type: boolean
external:
type: boolean
ProjectUser: ProjectUser:
type: object type: object
@ -133,10 +141,13 @@ definitions:
type: string type: string
username: username:
type: string type: string
role:
type: string
enum: [owner, manager, task_runner, guest]
ProjectBackup: ProjectBackup:
type: object type: object
example: {"meta":{"name":"homelab","alert":true,"alert_chat":"Test","max_parallel_tasks":0},"templates":[{"inventory":"Build","repository":"Demo","environment":"Empty","name":"Build","playbook":"build.yml","arguments":"[]","allow_override_args_in_task":false,"description":"Build Job","vault_key":null,"type":"build","start_version":"1.0.0","build_template":null,"view":"Build","autorun":false,"survey_vars":[],"suppress_success_alerts":false,"cron":"* * * * *"}],"repositories":[{"name":"Demo","git_url":"https://github.com/semaphoreui/demo-project.git","git_branch":"main","ssh_key":"None"}],"keys":[{"name":"None","type":"none"},{"name":"Vault Password","type":"login_password"}],"views":[{"title":"Build","position":0}],"inventories":[{"name":"Build","inventory":"","ssh_key":"None","become_key":"None","type":"static"},{"name":"Dev","inventory":"","ssh_key":"None","become_key":"None","type":"file"},{"name":"Prod","inventory":"","ssh_key":"None","become_key":"None","type":"file"}],"environments":[{"name":"Empty","password":null,"json":"{}","env":null}]} example: {"meta":{"name":"homelab","alert":true,"alert_chat":"Test","max_parallel_tasks":0,"type":null},"templates":[{"inventory":"Build","repository":"Demo","environment":"Empty","name":"Build","playbook":"build.yml","arguments":"[]","allow_override_args_in_task":false,"description":"Build Job","vault_key":null,"type":"build","start_version":"1.0.0","build_template":null,"view":"Build","autorun":false,"survey_vars":[],"suppress_success_alerts":false,"cron":"* * * * *"}],"repositories":[{"name":"Demo","git_url":"https://github.com/semaphoreui/demo-project.git","git_branch":"main","ssh_key":"None"}],"keys":[{"name":"None","type":"none"},{"name":"Vault Password","type":"login_password"}],"views":[{"title":"Build","position":0}],"inventories":[{"name":"Build","inventory":"","ssh_key":"None","become_key":"None","type":"static"},{"name":"Dev","inventory":"","ssh_key":"None","become_key":"None","type":"file"},{"name":"Prod","inventory":"","ssh_key":"None","become_key":"None","type":"file"}],"environments":[{"name":"Empty","password":null,"json":"{}","env":null}]}
properties: properties:
meta: meta:
type: object type: object
@ -152,6 +163,10 @@ definitions:
max_parallel_tasks: max_parallel_tasks:
type: integer type: integer
minimum: 0 minimum: 0
type:
type:
- string
- 'null'
templates: templates:
type: array type: array
items: items:
@ -304,6 +319,13 @@ definitions:
max_parallel_tasks: max_parallel_tasks:
type: integer type: integer
minimum: 0 minimum: 0
type:
type:
- string
- 'null'
demo:
description: Create Demo project resources?
type: boolean
Project: Project:
type: object type: object
properties: properties:
@ -326,10 +348,16 @@ definitions:
max_parallel_tasks: max_parallel_tasks:
type: integer type: integer
minimum: 0 minimum: 0
type:
type:
- string
- 'null'
AccessKeyRequest: AccessKeyRequest:
type: object type: object
properties: properties:
id:
type: integer
name: name:
type: string type: string
x-example: None x-example: None
@ -342,6 +370,8 @@ definitions:
type: integer type: integer
minimum: 1 minimum: 1
x-example: 2 x-example: 2
override_secret:
type: boolean
login_password: login_password:
type: object type: object
properties: properties:
@ -360,6 +390,10 @@ definitions:
type: string type: string
x-example: user x-example: user
example: user example: user
passphrase:
type: string
x-example: passphrase
example: passphrase
private_key: private_key:
type: string type: string
x-example: private key x-example: private key
@ -378,32 +412,40 @@ definitions:
enum: [none, ssh, login_password] enum: [none, ssh, login_password]
project_id: project_id:
type: integer type: integer
login_password:
type: object EnvironmentSecret:
properties: type: object
password: properties:
type: string id:
x-example: password type: integer
example: password name:
login: type: string
type: string type:
x-example: username type: string
example: username enum: [env, var]
ssh:
type: object EnvironmentSecretRequest:
properties: type: object
login: properties:
type: string id:
x-example: user type: integer
example: user name:
private_key: type: string
type: string secret:
x-example: private key type: string
example: private key type:
type: string
enum: [env, var]
operation:
type: string
enum: [create, update, delete]
EnvironmentRequest: EnvironmentRequest:
type: object type: object
properties: properties:
id:
type: integer
example: 1
name: name:
type: string type: string
example: Test example: Test
@ -418,6 +460,10 @@ definitions:
env: env:
type: string type: string
example: '{}' example: '{}'
secrets:
type: array
items:
$ref: '#/definitions/EnvironmentSecretRequest'
Environment: Environment:
type: object type: object
@ -439,10 +485,16 @@ definitions:
env: env:
type: string type: string
example: '{}' example: '{}'
secrets:
type: array
items:
$ref: '#/definitions/EnvironmentSecret'
InventoryRequest: InventoryRequest:
type: object type: object
properties: properties:
id:
type: integer
name: name:
type: string type: string
example: Test example: Test
@ -457,9 +509,12 @@ definitions:
become_key_id: become_key_id:
type: integer type: integer
minimum: 1 minimum: 1
repository_id:
type: integer
minimum: 1
type: type:
type: string type: string
enum: [static, static-yaml, file] enum: [static, static-yaml, file, terraform-workspace]
Inventory: Inventory:
type: object type: object
@ -477,9 +532,11 @@ definitions:
type: integer type: integer
become_key_id: become_key_id:
type: integer type: integer
repository_id:
type: integer
type: type:
type: string type: string
enum: [static, static-yaml, file] enum: [static, static-yaml, file, terraform-workspace]
Integration: Integration:
type: object type: object
@ -600,6 +657,8 @@ definitions:
RepositoryRequest: RepositoryRequest:
type: object type: object
properties: properties:
id:
type: integer
name: name:
type: string type: string
example: Test example: Test
@ -674,6 +733,9 @@ definitions:
TemplateRequest: TemplateRequest:
type: object type: object
properties: properties:
id:
type: integer
example: 1
project_id: project_id:
type: integer type: integer
minimum: 1 minimum: 1
@ -723,6 +785,16 @@ definitions:
type: array type: array
items: items:
$ref: "#/definitions/TemplateSurveyVar" $ref: "#/definitions/TemplateSurveyVar"
type:
type: string
enum: ["", build, deploy]
start_version:
type: string
build_template_id:
type: integer
autorun:
type: boolean
Template: Template:
type: object type: object
properties: properties:
@ -765,6 +837,28 @@ definitions:
git_branch: git_branch:
type: string type: string
example: main example: main
type:
type: string
enum: ["", build, deploy]
start_version:
type:
- string
- 'null'
build_template_id:
type:
- integer
- 'null'
autorun:
type: boolean
survey_vars:
type: array
items:
$ref: "#/definitions/TemplateSurveyVar"
vaults:
type: array
items:
$ref: "#/definitions/TemplateVault"
TemplateSurveyVar: TemplateSurveyVar:
type: object type: object
properties: properties:
@ -776,12 +870,28 @@ definitions:
type: string type: string
type: type:
type: string type: string
example: String => "", Integer => "int" enum: ["", int, enum, secret] # String => "", Integer => "int"
example: int
required: required:
type: boolean type: boolean
values:
type: array
items:
$ref: "#/definitions/TemplateSurveyVarValue"
TemplateSurveyVarValue:
type: object
properties:
name:
type: string
value:
type: string
TemplateVault: TemplateVault:
type: object type: object
properties: properties:
id:
type: integer
name: name:
type: string type: string
example: default example: default
@ -797,6 +907,7 @@ definitions:
type: type:
- string - string
- 'null' - 'null'
example: path/to/script-client.py
ScheduleRequest: ScheduleRequest:
type: object type: object
@ -1283,6 +1394,8 @@ paths:
responses: responses:
201: 201:
description: Created project description: Created project
schema:
$ref: "#/definitions/Project"
/projects/restore: /projects/restore:
post: post:
tags: tags:
@ -1344,10 +1457,12 @@ paths:
in: body in: body
required: true required: true
schema: schema:
type: object allOf:
properties: - $ref: '#/definitions/ProjectRequest'
name: - properties:
type: string id:
type: integer
minimum: 1
responses: responses:
204: 204:
description: Project saved description: Project saved
@ -1483,6 +1598,8 @@ paths:
enum: [owner, manager, task_runner, guest] enum: [owner, manager, task_runner, guest]
example: owner example: owner
summary: Update user role summary: Update user role
tags:
- project
responses: responses:
204: 204:
description: User updated description: User updated
@ -1704,8 +1821,10 @@ paths:
schema: schema:
$ref: "#/definitions/AccessKeyRequest" $ref: "#/definitions/AccessKeyRequest"
responses: responses:
204: 201:
description: Access Key created description: Access Key created
schema:
$ref: "#/definitions/AccessKey"
400: 400:
description: Bad type description: Bad type
/project/{project_id}/keys/{key_id}: /project/{project_id}/keys/{key_id}:
@ -1775,12 +1894,24 @@ paths:
schema: schema:
$ref: "#/definitions/RepositoryRequest" $ref: "#/definitions/RepositoryRequest"
responses: responses:
204: 201:
description: Repository created description: Repository created
schema:
$ref: "#/definitions/Repository"
/project/{project_id}/repositories/{repository_id}: /project/{project_id}/repositories/{repository_id}:
parameters: parameters:
- $ref: "#/parameters/project_id" - $ref: "#/parameters/project_id"
- $ref: "#/parameters/repository_id" - $ref: "#/parameters/repository_id"
get:
tags:
- project
summary: Get repository
responses:
200:
description: repository object
schema:
$ref: "#/definitions/Repository"
put: put:
tags: tags:
- project - project
@ -1851,6 +1982,15 @@ paths:
parameters: parameters:
- $ref: "#/parameters/project_id" - $ref: "#/parameters/project_id"
- $ref: "#/parameters/inventory_id" - $ref: "#/parameters/inventory_id"
get:
tags:
- project
summary: Get inventory
responses:
200:
description: inventory object
schema:
$ref: "#/definitions/Inventory"
put: put:
tags: tags:
- project - project
@ -1913,12 +2053,23 @@ paths:
schema: schema:
$ref: "#/definitions/EnvironmentRequest" $ref: "#/definitions/EnvironmentRequest"
responses: responses:
204: 201:
description: Environment created description: Environment created
schema:
$ref: "#/definitions/Environment"
/project/{project_id}/environment/{environment_id}: /project/{project_id}/environment/{environment_id}:
parameters: parameters:
- $ref: "#/parameters/project_id" - $ref: "#/parameters/project_id"
- $ref: "#/parameters/environment_id" - $ref: "#/parameters/environment_id"
get:
tags:
- project
summary: Get environment
responses:
200:
description: environment object
schema:
$ref: "#/definitions/Environment"
put: put:
tags: tags:
- project - project
@ -1980,7 +2131,7 @@ paths:
- project - project
summary: create template summary: create template
parameters: parameters:
- name: templateyes - name: template
in: body in: body
required: true required: true
schema: schema:
@ -1989,7 +2140,7 @@ paths:
201: 201:
description: template created description: template created
schema: schema:
$ref: "#/definitions/TemplateRequest" $ref: "#/definitions/Template"
/project/{project_id}/templates/{template_id}: /project/{project_id}/templates/{template_id}:
parameters: parameters:
- $ref: "#/parameters/project_id" - $ref: "#/parameters/project_id"

View File

@ -50,11 +50,18 @@ func updateEnvironmentSecrets(store db.Store, env db.Environment) error {
continue continue
} }
err = store.UpdateAccessKey(db.AccessKey{ updateKey := db.AccessKey{
Name: string(secret.Type) + "." + secret.Name, ID: key.ID,
String: secret.Secret, ProjectID: key.ProjectID,
Type: db.AccessKeyString, Name: string(secret.Type) + "." + secret.Name,
}) Type: db.AccessKeyString,
}
if secret.Secret != "" {
updateKey.String = secret.Secret
updateKey.OverrideSecret = true
}
err = store.UpdateAccessKey(updateKey)
} }
} }
@ -197,7 +204,16 @@ func AddEnvironment(w http.ResponseWriter, r *http.Request) {
//return //return
} }
w.WriteHeader(http.StatusNoContent) // Reload env
env, err = helpers.Store(r).GetEnvironment(newEnv.ProjectID, newEnv.ID)
if err != nil {
helpers.WriteError(w, err)
return
}
// Use empty array to avoid null in JSON
env.Secrets = []db.EnvironmentSecret{}
helpers.WriteJSON(w, http.StatusCreated, env)
} }
// RemoveEnvironment deletes an environment from the database // RemoveEnvironment deletes an environment from the database

View File

@ -101,7 +101,14 @@ func AddKey(w http.ResponseWriter, r *http.Request) {
Description: fmt.Sprintf("Access Key %s created", key.Name), Description: fmt.Sprintf("Access Key %s created", key.Name),
}) })
w.WriteHeader(http.StatusNoContent) // Reload key to drop sensitive fields
key, err = helpers.Store(r).GetAccessKey(*newKey.ProjectID, newKey.ID)
if err != nil {
helpers.WriteError(w, err)
return
}
helpers.WriteJSON(w, http.StatusCreated, key)
} }
// UpdateKey updates key in database // UpdateKey updates key in database

View File

@ -5,10 +5,10 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"github.com/gorilla/context"
"github.com/semaphoreui/semaphore/api/helpers" "github.com/semaphoreui/semaphore/api/helpers"
"github.com/semaphoreui/semaphore/db" "github.com/semaphoreui/semaphore/db"
"github.com/semaphoreui/semaphore/util" "github.com/semaphoreui/semaphore/util"
"github.com/gorilla/context"
) )
// RepositoryMiddleware ensures a repository exists and loads it to the context // RepositoryMiddleware ensures a repository exists and loads it to the context
@ -98,7 +98,7 @@ func AddRepository(w http.ResponseWriter, r *http.Request) {
Description: fmt.Sprintf("Repository %s created", repository.GitURL), Description: fmt.Sprintf("Repository %s created", repository.GitURL),
}) })
w.WriteHeader(http.StatusNoContent) helpers.WriteJSON(w, http.StatusCreated, newRepo)
} }
// UpdateRepository updates the values of a repository in the database // UpdateRepository updates the values of a repository in the database

View File

@ -11,8 +11,8 @@ func (d *SqlDb) CreateProject(project db.Project) (newProject db.Project, err er
insertId, err := d.insert( insertId, err := d.insert(
"id", "id",
"insert into project(name, created, type) values (?, ?, ?)", "insert into project(name, created, type, alert, alert_chat, max_parallel_tasks) values (?, ?, ?, ?, ?, ?)",
project.Name, project.Created, project.Type) project.Name, project.Created, project.Type, project.Alert, project.AlertChat, project.MaxParallelTasks)
if err != nil { if err != nil {
return return