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

Skip to content

Commit e17fd0b

Browse files
feat: Add GET previous template version endpoint (#5230)
1 parent 84872d9 commit e17fd0b

File tree

9 files changed

+212
-5
lines changed

9 files changed

+212
-5
lines changed

coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ func New(options *Options) *API {
349349
r.Route("/templateversions", func(r chi.Router) {
350350
r.Post("/", api.postTemplateVersionsByOrganization)
351351
r.Get("/{templateversionname}", api.templateVersionByOrganizationAndName)
352+
r.Get("/{templateversionname}/previous", api.previousTemplateVersionByOrganizationAndName)
352353
})
353354
r.Route("/templates", func(r chi.Router) {
354355
r.Post("/", api.postTemplateByOrganization)

coderd/coderdtest/authorize.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,12 @@ func AGPLRoutes(a *AuthTester) (map[string]string, map[string]RouteCheck) {
236236
"GET:/api/v2/applications/auth-redirect": {AssertAction: rbac.ActionCreate, AssertObject: rbac.ResourceAPIKey},
237237

238238
// These endpoints need payloads to get to the auth part. Payloads will be required
239-
"PUT:/api/v2/users/{user}/roles": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
240-
"PUT:/api/v2/organizations/{organization}/members/{user}/roles": {NoAuthorize: true},
241-
"POST:/api/v2/workspaces/{workspace}/builds": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
242-
"POST:/api/v2/organizations/{organization}/templateversions": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
243-
"GET:/api/v2/organizations/{organization}/templateversions/{templateversionname}": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
239+
"PUT:/api/v2/users/{user}/roles": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
240+
"PUT:/api/v2/organizations/{organization}/members/{user}/roles": {NoAuthorize: true},
241+
"POST:/api/v2/workspaces/{workspace}/builds": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
242+
"POST:/api/v2/organizations/{organization}/templateversions": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
243+
"GET:/api/v2/organizations/{organization}/templateversions/{templateversionname}": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
244+
"GET:/api/v2/organizations/{organization}/templateversions/{templateversionname}/previous": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
244245

245246
// Endpoints that use the SQLQuery filter.
246247
"GET:/api/v2/workspaces/": {

coderd/database/databasefake/databasefake.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,6 +1718,46 @@ func (q *fakeQuerier) GetTemplateVersionByJobID(_ context.Context, jobID uuid.UU
17181718
return database.TemplateVersion{}, sql.ErrNoRows
17191719
}
17201720

1721+
func (q *fakeQuerier) GetPreviousTemplateVersion(_ context.Context, arg database.GetPreviousTemplateVersionParams) (database.TemplateVersion, error) {
1722+
q.mutex.RLock()
1723+
defer q.mutex.RUnlock()
1724+
1725+
var currentTemplateVersion database.TemplateVersion
1726+
for _, templateVersion := range q.templateVersions {
1727+
if templateVersion.TemplateID != arg.TemplateID {
1728+
continue
1729+
}
1730+
if templateVersion.Name != arg.Name {
1731+
continue
1732+
}
1733+
if templateVersion.OrganizationID != arg.OrganizationID {
1734+
continue
1735+
}
1736+
currentTemplateVersion = templateVersion
1737+
break
1738+
}
1739+
1740+
previousTemplateVersions := make([]database.TemplateVersion, 0)
1741+
for _, templateVersion := range q.templateVersions {
1742+
if templateVersion.ID == currentTemplateVersion.ID {
1743+
continue
1744+
}
1745+
if templateVersion.CreatedAt.Before(currentTemplateVersion.CreatedAt) {
1746+
previousTemplateVersions = append(previousTemplateVersions, templateVersion)
1747+
}
1748+
}
1749+
1750+
if len(previousTemplateVersions) == 0 {
1751+
return database.TemplateVersion{}, sql.ErrNoRows
1752+
}
1753+
1754+
sort.Slice(previousTemplateVersions, func(i, j int) bool {
1755+
return previousTemplateVersions[i].CreatedAt.After(previousTemplateVersions[j].CreatedAt)
1756+
})
1757+
1758+
return previousTemplateVersions[0], nil
1759+
}
1760+
17211761
func (q *fakeQuerier) GetParameterSchemasByJobID(_ context.Context, jobID uuid.UUID) ([]database.ParameterSchema, error) {
17221762
q.mutex.RLock()
17231763
defer q.mutex.RUnlock()

coderd/database/querier.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/templateversions.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,17 @@ SET
110110
updated_at = $3
111111
WHERE
112112
job_id = $1;
113+
114+
-- name: GetPreviousTemplateVersion :one
115+
SELECT
116+
*
117+
FROM
118+
template_versions
119+
WHERE
120+
created_at < (
121+
SELECT created_at
122+
FROM template_versions AS tv
123+
WHERE tv.organization_id = $1 AND tv.name = $2 AND tv.template_id = $3
124+
)
125+
ORDER BY created_at DESC
126+
LIMIT 1;

coderd/templateversions.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,71 @@ func (api *API) templateVersionByOrganizationAndName(rw http.ResponseWriter, r *
640640
httpapi.Write(ctx, rw, http.StatusOK, convertTemplateVersion(templateVersion, convertProvisionerJob(job), user))
641641
}
642642

643+
func (api *API) previousTemplateVersionByOrganizationAndName(rw http.ResponseWriter, r *http.Request) {
644+
ctx := r.Context()
645+
organization := httpmw.OrganizationParam(r)
646+
templateVersionName := chi.URLParam(r, "templateversionname")
647+
templateVersion, err := api.Database.GetTemplateVersionByOrganizationAndName(ctx, database.GetTemplateVersionByOrganizationAndNameParams{
648+
OrganizationID: organization.ID,
649+
Name: templateVersionName,
650+
})
651+
if err != nil {
652+
if xerrors.Is(err, sql.ErrNoRows) {
653+
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
654+
Message: fmt.Sprintf("No template version found by name %q.", templateVersionName),
655+
})
656+
return
657+
}
658+
659+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
660+
Message: "Internal error fetching template version.",
661+
Detail: err.Error(),
662+
})
663+
return
664+
}
665+
666+
previousTemplateVersion, err := api.Database.GetPreviousTemplateVersion(ctx, database.GetPreviousTemplateVersionParams{
667+
OrganizationID: organization.ID,
668+
Name: templateVersionName,
669+
TemplateID: templateVersion.TemplateID,
670+
})
671+
672+
if err != nil {
673+
if xerrors.Is(err, sql.ErrNoRows) {
674+
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
675+
Message: fmt.Sprintf("No previous template version found for %q.", templateVersionName),
676+
})
677+
return
678+
}
679+
680+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
681+
Message: "Internal error fetching the previous template version.",
682+
Detail: err.Error(),
683+
})
684+
return
685+
}
686+
687+
job, err := api.Database.GetProvisionerJobByID(ctx, previousTemplateVersion.JobID)
688+
if err != nil {
689+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
690+
Message: "Internal error fetching provisioner job.",
691+
Detail: err.Error(),
692+
})
693+
return
694+
}
695+
696+
user, err := api.Database.GetUserByID(ctx, templateVersion.CreatedBy)
697+
if err != nil {
698+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
699+
Message: "Internal error on fetching user.",
700+
Detail: err.Error(),
701+
})
702+
return
703+
}
704+
705+
httpapi.Write(ctx, rw, http.StatusOK, convertTemplateVersion(previousTemplateVersion, convertProvisionerJob(job), user))
706+
}
707+
643708
func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Request) {
644709
var (
645710
ctx = r.Context()

coderd/templateversions_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,3 +963,37 @@ func TestTemplateVersionByOrganizationAndName(t *testing.T) {
963963
require.NoError(t, err)
964964
})
965965
}
966+
967+
func TestPreviousTemplateVersion(t *testing.T) {
968+
t.Parallel()
969+
t.Run("Previous version not found", func(t *testing.T) {
970+
t.Parallel()
971+
client := coderdtest.New(t, nil)
972+
user := coderdtest.CreateFirstUser(t, client)
973+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
974+
975+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
976+
defer cancel()
977+
978+
_, err := client.PreviousTemplateVersion(ctx, user.OrganizationID, version.Name)
979+
var apiErr *codersdk.Error
980+
require.ErrorAs(t, err, &apiErr)
981+
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
982+
})
983+
984+
t.Run("Previous version found", func(t *testing.T) {
985+
t.Parallel()
986+
client := coderdtest.New(t, nil)
987+
user := coderdtest.CreateFirstUser(t, client)
988+
previousVersion := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
989+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, previousVersion.ID)
990+
latestVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, nil, template.ID)
991+
992+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
993+
defer cancel()
994+
995+
result, err := client.PreviousTemplateVersion(ctx, user.OrganizationID, latestVersion.Name)
996+
require.NoError(t, err)
997+
require.Equal(t, previousVersion.ID, result.ID)
998+
})
999+
}

codersdk/templateversions.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,16 @@ func (c *Client) CancelTemplateVersionDryRun(ctx context.Context, version, job u
182182
}
183183
return nil
184184
}
185+
186+
func (c *Client) PreviousTemplateVersion(ctx context.Context, organization uuid.UUID, versionName string) (TemplateVersion, error) {
187+
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/templateversions/%s/previous", organization, versionName), nil)
188+
if err != nil {
189+
return TemplateVersion{}, err
190+
}
191+
defer res.Body.Close()
192+
if res.StatusCode != http.StatusOK {
193+
return TemplateVersion{}, readBodyAsError(res)
194+
}
195+
var version TemplateVersion
196+
return version, json.NewDecoder(res.Body).Decode(&version)
197+
}

0 commit comments

Comments
 (0)