Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e903072

Browse files
committed
Add parameters
1 parent bdea345 commit e903072

16 files changed

+479
-189
lines changed

coderd/coderd.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,14 @@ func New(options *Options) (http.Handler, func()) {
5959
r.Get("/", api.projectsByOrganization)
6060
r.Get("/{projectname}", api.projectByOrganizationAndName)
6161
})
62-
r.Get("/parameters", nil)
63-
r.Post("/parameters", nil)
64-
r.Patch("/parameters/{name}", nil)
65-
r.Delete("/parameters/{name}", nil)
62+
})
63+
r.Route("/parameters/{scope}/{id}", func(r chi.Router) {
64+
r.Use(httpmw.ExtractAPIKey(options.Database, nil))
65+
r.Post("/", api.postParameter)
66+
r.Get("/", api.parameters)
67+
r.Route("/{name}", func(r chi.Router) {
68+
r.Delete("/", api.deleteParameter)
69+
})
6670
})
6771
r.Route("/projects/{project}", func(r chi.Router) {
6872
r.Use(
@@ -71,10 +75,6 @@ func New(options *Options) (http.Handler, func()) {
7175
httpmw.ExtractOrganizationParam(options.Database),
7276
)
7377
r.Get("/", api.project)
74-
r.Get("/parameters", api.parametersByProject)
75-
r.Post("/parameters", api.postParametersByProject)
76-
r.Patch("/parameters/{name}", nil)
77-
r.Delete("/parameters/{name}", nil)
7878
r.Route("/versions", func(r chi.Router) {
7979
r.Get("/", api.projectVersionsByProject)
8080
r.Patch("/versions", nil)

coderd/organizations.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ type CreateProjectVersionRequest struct {
3636
Provisioner database.ProvisionerType `json:"provisioner" validate:"oneof=terraform echo,required"`
3737
// ParameterValues allows for additional parameters to be provided
3838
// during the dry-run provision stage.
39-
ParameterValues []CreateParameterValueRequest `json:"parameter_values"`
39+
ParameterValues []CreateParameterRequest `json:"parameter_values"`
4040
}
4141

4242
// CreateProjectRequest provides options when creating a project.

coderd/organizations_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func TestPostProjectVersionsByOrganization(t *testing.T) {
8181
StorageMethod: database.ProvisionerStorageMethodFile,
8282
StorageSource: file.Hash,
8383
Provisioner: database.ProvisionerTypeEcho,
84-
ParameterValues: []coderd.CreateParameterValueRequest{{
84+
ParameterValues: []coderd.CreateParameterRequest{{
8585
Name: "example",
8686
SourceValue: "value",
8787
SourceScheme: database.ParameterSourceSchemeData,

coderd/parameters.go

+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package coderd
2+
3+
import (
4+
"database/sql"
5+
"errors"
6+
"fmt"
7+
"net/http"
8+
"time"
9+
10+
"github.com/go-chi/chi/v5"
11+
"github.com/go-chi/render"
12+
"github.com/google/uuid"
13+
14+
"github.com/coder/coder/database"
15+
"github.com/coder/coder/httpapi"
16+
)
17+
18+
type ParameterScope string
19+
20+
const (
21+
ParameterOrganization ParameterScope = "organization"
22+
ParameterProject ParameterScope = "project"
23+
ParameterUser ParameterScope = "user"
24+
ParameterWorkspace ParameterScope = "workspace"
25+
)
26+
27+
// Parameter represents a set value for the scope.
28+
type Parameter struct {
29+
ID uuid.UUID `db:"id" json:"id"`
30+
CreatedAt time.Time `db:"created_at" json:"created_at"`
31+
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
32+
Scope ParameterScope `db:"scope" json:"scope"`
33+
ScopeID string `db:"scope_id" json:"scope_id"`
34+
Name string `db:"name" json:"name"`
35+
SourceScheme database.ParameterSourceScheme `db:"source_scheme" json:"source_scheme"`
36+
DestinationScheme database.ParameterDestinationScheme `db:"destination_scheme" json:"destination_scheme"`
37+
}
38+
39+
// CreateParameterRequest is used to create a new parameter value for a scope.
40+
type CreateParameterRequest struct {
41+
Name string `json:"name" validate:"required"`
42+
SourceValue string `json:"source_value" validate:"required"`
43+
SourceScheme database.ParameterSourceScheme `json:"source_scheme" validate:"oneof=data,required"`
44+
DestinationScheme database.ParameterDestinationScheme `json:"destination_scheme" validate:"oneof=environment_variable provisioner_variable,required"`
45+
}
46+
47+
func (api *api) postParameter(rw http.ResponseWriter, r *http.Request) {
48+
var createRequest CreateParameterRequest
49+
if !httpapi.Read(rw, r, &createRequest) {
50+
return
51+
}
52+
scope, id, valid := readScopeAndID(rw, r)
53+
if !valid {
54+
return
55+
}
56+
parameterValue, err := api.Database.GetParameterValueByScopeAndName(r.Context(), database.GetParameterValueByScopeAndNameParams{
57+
Scope: scope,
58+
ScopeID: id,
59+
Name: createRequest.Name,
60+
})
61+
if err == nil {
62+
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
63+
Message: fmt.Sprintf("a parameter already exists in scope %q with name %q", scope, createRequest.Name),
64+
})
65+
return
66+
}
67+
if !errors.Is(err, sql.ErrNoRows) {
68+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
69+
Message: fmt.Sprintf("get parameter value: %s", err),
70+
})
71+
return
72+
}
73+
parameterValue, err = api.Database.InsertParameterValue(r.Context(), database.InsertParameterValueParams{
74+
ID: uuid.New(),
75+
Name: createRequest.Name,
76+
CreatedAt: database.Now(),
77+
UpdatedAt: database.Now(),
78+
Scope: scope,
79+
ScopeID: id,
80+
SourceScheme: createRequest.SourceScheme,
81+
SourceValue: createRequest.SourceValue,
82+
DestinationScheme: createRequest.DestinationScheme,
83+
})
84+
if err != nil {
85+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
86+
Message: fmt.Sprintf("insert parameter value: %s", err),
87+
})
88+
return
89+
}
90+
91+
render.Status(r, http.StatusCreated)
92+
render.JSON(rw, r, convertParameterValue(parameterValue))
93+
}
94+
95+
func (api *api) parameters(rw http.ResponseWriter, r *http.Request) {
96+
scope, id, valid := readScopeAndID(rw, r)
97+
if !valid {
98+
return
99+
}
100+
parameterValues, err := api.Database.GetParameterValuesByScope(r.Context(), database.GetParameterValuesByScopeParams{
101+
Scope: scope,
102+
ScopeID: id,
103+
})
104+
if errors.Is(err, sql.ErrNoRows) {
105+
err = nil
106+
}
107+
if err != nil {
108+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
109+
Message: fmt.Sprintf("get parameter values by scope: %s", err),
110+
})
111+
return
112+
}
113+
apiParameterValues := make([]Parameter, 0, len(parameterValues))
114+
for _, parameterValue := range parameterValues {
115+
apiParameterValues = append(apiParameterValues, convertParameterValue(parameterValue))
116+
}
117+
118+
render.Status(r, http.StatusOK)
119+
render.JSON(rw, r, apiParameterValues)
120+
}
121+
122+
func (api *api) deleteParameter(rw http.ResponseWriter, r *http.Request) {
123+
scope, id, valid := readScopeAndID(rw, r)
124+
if !valid {
125+
return
126+
}
127+
name := chi.URLParam(r, "name")
128+
parameterValue, err := api.Database.GetParameterValueByScopeAndName(r.Context(), database.GetParameterValueByScopeAndNameParams{
129+
Scope: scope,
130+
ScopeID: id,
131+
Name: name,
132+
})
133+
if errors.Is(err, sql.ErrNoRows) {
134+
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
135+
Message: fmt.Sprintf("parameter doesn't exist in the provided scope with name %q", name),
136+
})
137+
return
138+
}
139+
if err != nil {
140+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
141+
Message: fmt.Sprintf("get parameter value: %s", err),
142+
})
143+
return
144+
}
145+
err = api.Database.DeleteParameterValueByID(r.Context(), parameterValue.ID)
146+
if err != nil {
147+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
148+
Message: fmt.Sprintf("delete parameter: %s", err),
149+
})
150+
return
151+
}
152+
httpapi.Write(rw, http.StatusOK, httpapi.Response{
153+
Message: "parameter deleted",
154+
})
155+
}
156+
157+
func convertParameterValue(parameterValue database.ParameterValue) Parameter {
158+
return Parameter{
159+
ID: parameterValue.ID,
160+
CreatedAt: parameterValue.CreatedAt,
161+
UpdatedAt: parameterValue.UpdatedAt,
162+
Scope: ParameterScope(parameterValue.Scope),
163+
ScopeID: parameterValue.ScopeID,
164+
Name: parameterValue.Name,
165+
SourceScheme: parameterValue.SourceScheme,
166+
DestinationScheme: parameterValue.DestinationScheme,
167+
}
168+
}
169+
170+
func readScopeAndID(rw http.ResponseWriter, r *http.Request) (scope database.ParameterScope, id string, valid bool) {
171+
switch chi.URLParam(r, "scope") {
172+
case string(ParameterOrganization):
173+
scope = database.ParameterScopeOrganization
174+
case string(ParameterProject):
175+
scope = database.ParameterScopeProject
176+
case string(ParameterUser):
177+
scope = database.ParameterScopeUser
178+
case string(ParameterWorkspace):
179+
scope = database.ParameterScopeWorkspace
180+
default:
181+
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
182+
Message: fmt.Sprintf("invalid scope %q", scope),
183+
})
184+
return
185+
}
186+
187+
id = chi.URLParam(r, "id")
188+
valid = true
189+
return
190+
}

coderd/parameters_test.go

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package coderd_test
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"testing"
7+
8+
"github.com/coder/coder/coderd"
9+
"github.com/coder/coder/coderd/coderdtest"
10+
"github.com/coder/coder/codersdk"
11+
"github.com/coder/coder/database"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestPostParameter(t *testing.T) {
16+
t.Parallel()
17+
t.Run("BadScope", func(t *testing.T) {
18+
t.Parallel()
19+
client := coderdtest.New(t, nil)
20+
user := coderdtest.CreateFirstUser(t, client)
21+
_, err := client.CreateParameter(context.Background(), coderd.ParameterScope("something"), user.OrganizationID, coderd.CreateParameterRequest{
22+
Name: "example",
23+
SourceValue: "tomato",
24+
SourceScheme: database.ParameterSourceSchemeData,
25+
DestinationScheme: database.ParameterDestinationSchemeProvisionerVariable,
26+
})
27+
var apiErr *codersdk.Error
28+
require.ErrorAs(t, err, &apiErr)
29+
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
30+
})
31+
32+
t.Run("Create", func(t *testing.T) {
33+
t.Parallel()
34+
client := coderdtest.New(t, nil)
35+
user := coderdtest.CreateFirstUser(t, client)
36+
_, err := client.CreateParameter(context.Background(), coderd.ParameterOrganization, user.OrganizationID, coderd.CreateParameterRequest{
37+
Name: "example",
38+
SourceValue: "tomato",
39+
SourceScheme: database.ParameterSourceSchemeData,
40+
DestinationScheme: database.ParameterDestinationSchemeProvisionerVariable,
41+
})
42+
require.NoError(t, err)
43+
})
44+
45+
t.Run("AlreadyExists", func(t *testing.T) {
46+
t.Parallel()
47+
client := coderdtest.New(t, nil)
48+
user := coderdtest.CreateFirstUser(t, client)
49+
_, err := client.CreateParameter(context.Background(), coderd.ParameterOrganization, user.OrganizationID, coderd.CreateParameterRequest{
50+
Name: "example",
51+
SourceValue: "tomato",
52+
SourceScheme: database.ParameterSourceSchemeData,
53+
DestinationScheme: database.ParameterDestinationSchemeProvisionerVariable,
54+
})
55+
require.NoError(t, err)
56+
57+
_, err = client.CreateParameter(context.Background(), coderd.ParameterOrganization, user.OrganizationID, coderd.CreateParameterRequest{
58+
Name: "example",
59+
SourceValue: "tomato",
60+
SourceScheme: database.ParameterSourceSchemeData,
61+
DestinationScheme: database.ParameterDestinationSchemeProvisionerVariable,
62+
})
63+
var apiErr *codersdk.Error
64+
require.ErrorAs(t, err, &apiErr)
65+
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
66+
})
67+
}
68+
69+
func TestParameters(t *testing.T) {
70+
t.Parallel()
71+
t.Run("ListEmpty", func(t *testing.T) {
72+
t.Parallel()
73+
client := coderdtest.New(t, nil)
74+
user := coderdtest.CreateFirstUser(t, client)
75+
_, err := client.Parameters(context.Background(), coderd.ParameterOrganization, user.OrganizationID)
76+
require.NoError(t, err)
77+
})
78+
t.Run("List", func(t *testing.T) {
79+
t.Parallel()
80+
client := coderdtest.New(t, nil)
81+
user := coderdtest.CreateFirstUser(t, client)
82+
_, err := client.CreateParameter(context.Background(), coderd.ParameterOrganization, user.OrganizationID, coderd.CreateParameterRequest{
83+
Name: "example",
84+
SourceValue: "tomato",
85+
SourceScheme: database.ParameterSourceSchemeData,
86+
DestinationScheme: database.ParameterDestinationSchemeProvisionerVariable,
87+
})
88+
require.NoError(t, err)
89+
params, err := client.Parameters(context.Background(), coderd.ParameterOrganization, user.OrganizationID)
90+
require.NoError(t, err)
91+
require.Len(t, params, 1)
92+
})
93+
}
94+
95+
func TestDeleteParameter(t *testing.T) {
96+
t.Parallel()
97+
t.Run("NotExist", func(t *testing.T) {
98+
t.Parallel()
99+
client := coderdtest.New(t, nil)
100+
user := coderdtest.CreateFirstUser(t, client)
101+
err := client.DeleteParameter(context.Background(), coderd.ParameterOrganization, user.OrganizationID, "something")
102+
var apiErr *codersdk.Error
103+
require.ErrorAs(t, err, &apiErr)
104+
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
105+
})
106+
t.Run("Delete", func(t *testing.T) {
107+
t.Parallel()
108+
client := coderdtest.New(t, nil)
109+
user := coderdtest.CreateFirstUser(t, client)
110+
param, err := client.CreateParameter(context.Background(), coderd.ParameterOrganization, user.OrganizationID, coderd.CreateParameterRequest{
111+
Name: "example",
112+
SourceValue: "tomato",
113+
SourceScheme: database.ParameterSourceSchemeData,
114+
DestinationScheme: database.ParameterDestinationSchemeProvisionerVariable,
115+
})
116+
require.NoError(t, err)
117+
err = client.DeleteParameter(context.Background(), coderd.ParameterOrganization, user.OrganizationID, param.Name)
118+
require.NoError(t, err)
119+
})
120+
}

0 commit comments

Comments
 (0)