Skip to content

Commit

Permalink
feat: Add template version page (#5071)
Browse files Browse the repository at this point in the history
  • Loading branch information
BrunoQuaresma committed Nov 15, 2022
1 parent 773fc73 commit f262fb4
Show file tree
Hide file tree
Showing 34 changed files with 1,769 additions and 843 deletions.
5 changes: 4 additions & 1 deletion coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,10 @@ func New(options *Options) *API {
httpmw.ExtractOrganizationParam(options.Database),
)
r.Get("/", api.organization)
r.Post("/templateversions", api.postTemplateVersionsByOrganization)
r.Route("/templateversions", func(r chi.Router) {
r.Post("/", api.postTemplateVersionsByOrganization)
r.Get("/{templateversionname}", api.templateVersionByOrganizationAndName)
})
r.Route("/templates", func(r chi.Router) {
r.Post("/", api.postTemplateByOrganization)
r.Get("/", api.templatesByOrganization)
Expand Down
9 changes: 5 additions & 4 deletions coderd/coderdtest/authorize.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,11 @@ func AGPLRoutes(a *AuthTester) (map[string]string, map[string]RouteCheck) {
"GET:/api/v2/applications/auth-redirect": {AssertAction: rbac.ActionCreate, AssertObject: rbac.ResourceAPIKey},

// These endpoints need payloads to get to the auth part. Payloads will be required
"PUT:/api/v2/users/{user}/roles": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
"PUT:/api/v2/organizations/{organization}/members/{user}/roles": {NoAuthorize: true},
"POST:/api/v2/workspaces/{workspace}/builds": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
"POST:/api/v2/organizations/{organization}/templateversions": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
"PUT:/api/v2/users/{user}/roles": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
"PUT:/api/v2/organizations/{organization}/members/{user}/roles": {NoAuthorize: true},
"POST:/api/v2/workspaces/{workspace}/builds": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
"POST:/api/v2/organizations/{organization}/templateversions": {StatusCode: http.StatusBadRequest, NoAuthorize: true},
"GET:/api/v2/organizations/{organization}/templateversions/{templateversionname}": {StatusCode: http.StatusBadRequest, NoAuthorize: true},

// Endpoints that use the SQLQuery filter.
"GET:/api/v2/workspaces/": {StatusCode: http.StatusOK, NoAuthorize: true},
Expand Down
16 changes: 16 additions & 0 deletions coderd/database/databasefake/databasefake.go
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,22 @@ func (q *fakeQuerier) GetTemplateVersionByTemplateIDAndName(_ context.Context, a
return database.TemplateVersion{}, sql.ErrNoRows
}

func (q *fakeQuerier) GetTemplateVersionByOrganizationAndName(_ context.Context, arg database.GetTemplateVersionByOrganizationAndNameParams) (database.TemplateVersion, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

for _, templateVersion := range q.templateVersions {
if templateVersion.OrganizationID != arg.OrganizationID {
continue
}
if !strings.EqualFold(templateVersion.Name, arg.Name) {
continue
}
return templateVersion, nil
}
return database.TemplateVersion{}, sql.ErrNoRows
}

func (q *fakeQuerier) GetTemplateVersionByID(_ context.Context, templateVersionID uuid.UUID) (database.TemplateVersion, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
Expand Down
1 change: 1 addition & 0 deletions coderd/database/querier.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions coderd/database/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions coderd/database/queries/templateversions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ WHERE
template_id = $1
AND "name" = $2;

-- name: GetTemplateVersionByOrganizationAndName :one
SELECT
*
FROM
template_versions
WHERE
organization_id = $1
AND "name" = $2;

-- name: GetTemplateVersionByID :one
SELECT
*
Expand Down
42 changes: 42 additions & 0 deletions coderd/templateversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,48 @@ func (api *API) templateVersionByName(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, http.StatusOK, convertTemplateVersion(templateVersion, convertProvisionerJob(job), user))
}

func (api *API) templateVersionByOrganizationAndName(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
organization := httpmw.OrganizationParam(r)
templateVersionName := chi.URLParam(r, "templateversionname")
templateVersion, err := api.Database.GetTemplateVersionByOrganizationAndName(ctx, database.GetTemplateVersionByOrganizationAndNameParams{
OrganizationID: organization.ID,
Name: templateVersionName,
})
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
Message: fmt.Sprintf("No template version found by name %q.", templateVersionName),
})
return
}
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching template version.",
Detail: err.Error(),
})
return
}
job, err := api.Database.GetProvisionerJobByID(ctx, templateVersion.JobID)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching provisioner job.",
Detail: err.Error(),
})
return
}

user, err := api.Database.GetUserByID(ctx, templateVersion.CreatedBy)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error on fetching user.",
Detail: err.Error(),
})
return
}

httpapi.Write(ctx, rw, http.StatusOK, convertTemplateVersion(templateVersion, convertProvisionerJob(job), user))
}

func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
Expand Down
33 changes: 33 additions & 0 deletions coderd/templateversions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -928,3 +928,36 @@ func TestPaginatedTemplateVersions(t *testing.T) {
})
}
}

func TestTemplateVersionByOrganizationAndName(t *testing.T) {
t.Parallel()
t.Run("NotFound", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)

ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()

_, err := client.TemplateVersionByOrganizationAndName(ctx, user.OrganizationID, "nothing")
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
})

t.Run("Found", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)

ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()

_, err := client.TemplateVersionByOrganizationAndName(ctx, user.OrganizationID, version.Name)
require.NoError(t, err)
})
}
19 changes: 19 additions & 0 deletions codersdk/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,25 @@ func (c *Client) CreateTemplateVersion(ctx context.Context, organizationID uuid.
return templateVersion, json.NewDecoder(res.Body).Decode(&templateVersion)
}

func (c *Client) TemplateVersionByOrganizationAndName(ctx context.Context, organizationID uuid.UUID, name string) (TemplateVersion, error) {
res, err := c.Request(ctx, http.MethodGet,
fmt.Sprintf("/api/v2/organizations/%s/templateversions/%s", organizationID.String(), name),
nil,
)

if err != nil {
return TemplateVersion{}, xerrors.Errorf("execute request: %w", err)
}
defer res.Body.Close()

if res.StatusCode != http.StatusOK {
return TemplateVersion{}, readBodyAsError(res)
}

var templateVersion TemplateVersion
return templateVersion, json.NewDecoder(res.Body).Decode(&templateVersion)
}

// CreateTemplate creates a new template inside an organization.
func (c *Client) CreateTemplate(ctx context.Context, organizationID uuid.UUID, request CreateTemplateRequest) (Template, error) {
res, err := c.Request(ctx, http.MethodPost,
Expand Down
16 changes: 16 additions & 0 deletions site/js-untar.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
declare module "js-untar" {
interface File {
name: string
readAsString: () => string
}

const Untar: (buffer: ArrayBuffer) => {
then: (
resolve?: () => Promise<void>,
reject?: () => Promise<void>,
progress: (file: File) => Promise<void>,
) => Promise<void>
}

export default Untar
}
1 change: 1 addition & 0 deletions site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"front-matter": "4.0.2",
"history": "5.3.0",
"i18next": "21.9.1",
"js-untar": "2.0.0",
"just-debounce-it": "3.1.1",
"react": "18.2.0",
"react-chartjs-2": "4.3.1",
Expand Down
31 changes: 21 additions & 10 deletions site/src/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ const NetworkSettingsPage = lazy(
() => import("./pages/DeploySettingsPage/NetworkSettingsPage"),
)
const GitAuthPage = lazy(() => import("./pages/GitAuthPage/GitAuthPage"))
const TemplateVersionPage = lazy(
() => import("./pages/TemplateVersionPage/TemplateVersionPage"),
)

export const AppRouter: FC = () => {
const xServices = useContext(XServiceContext)
Expand Down Expand Up @@ -123,16 +126,14 @@ export const AppRouter: FC = () => {
}
/>

<Route path="workspaces">
<Route
index
element={
<AuthAndFrame>
<WorkspacesPage />
</AuthAndFrame>
}
/>
</Route>
<Route
path="workspaces"
element={
<AuthAndFrame>
<WorkspacesPage />
</AuthAndFrame>
}
/>

<Route path="templates">
<Route
Expand Down Expand Up @@ -181,6 +182,16 @@ export const AppRouter: FC = () => {
</RequireAuth>
}
/>
<Route path="versions">
<Route
path=":version"
element={
<AuthAndFrame>
<TemplateVersionPage />
</AuthAndFrame>
}
/>
</Route>
</Route>
</Route>

Expand Down
1 change: 1 addition & 0 deletions site/src/__mocks__/js-untar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default jest.fn()
17 changes: 17 additions & 0 deletions site/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,16 @@ export const getTemplateVersions = async (
return response.data
}

export const getTemplateVersionByName = async (
organizationId: string,
versionName: string,
): Promise<TypesGen.TemplateVersion> => {
const response = await axios.get<TypesGen.TemplateVersion>(
`/api/v2/organizations/${organizationId}/templateversions/${versionName}`,
)
return response.data
}

export const updateTemplateMeta = async (
templateId: string,
data: TypesGen.UpdateTemplateMeta,
Expand Down Expand Up @@ -646,3 +656,10 @@ export const getReplicas = async (): Promise<TypesGen.Replica[]> => {
const response = await axios.get(`/api/v2/replicas`)
return response.data
}

export const getFile = async (fileId: string): Promise<ArrayBuffer> => {
const response = await axios.get<ArrayBuffer>(`/api/v2/files/${fileId}`, {
responseType: "arraybuffer",
})
return response.data
}
21 changes: 21 additions & 0 deletions site/src/components/Icons/MarkdownIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"

export const MarkdownIcon = (props: SvgIconProps): JSX.Element => (
<SvgIcon {...props} viewBox="0 0 32 32">
<rect
x="2.5"
y="7.955"
width="27"
height="16.091"
style={{ fill: "none", stroke: "#755838" }}
/>
<polygon
points="5.909 20.636 5.909 11.364 8.636 11.364 11.364 14.773 14.091 11.364 16.818 11.364 16.818 20.636 14.091 20.636 14.091 15.318 11.364 18.727 8.636 15.318 8.636 20.636 5.909 20.636"
style={{ stroke: "#755838" }}
/>
<polygon
points="22.955 20.636 18.864 16.136 21.591 16.136 21.591 11.364 24.318 11.364 24.318 16.136 27.045 16.136 22.955 20.636"
style={{ stroke: "#755838" }}
/>
</SvgIcon>
)
22 changes: 22 additions & 0 deletions site/src/components/Icons/TerraformIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"

export const TerraformIcon = (props: SvgIconProps): JSX.Element => (
<SvgIcon {...props} viewBox="0 0 32 32">
<polygon
points="12.042 6.858 20.071 11.448 20.071 20.462 12.042 15.868 12.042 6.858 12.042 6.858"
style={{ fill: "#813cf3" }}
/>
<polygon
points="20.5 20.415 28.459 15.84 28.459 6.887 20.5 11.429 20.5 20.415 20.5 20.415"
style={{ fill: "#813cf3" }}
/>
<polygon
points="3.541 11.01 11.571 15.599 11.571 6.59 3.541 2 3.541 11.01 3.541 11.01"
style={{ fill: "#813cf3" }}
/>
<polygon
points="12.042 25.41 20.071 30 20.071 20.957 12.042 16.368 12.042 25.41 12.042 25.41"
style={{ fill: "#813cf3" }}
/>
</SvgIcon>
)

0 comments on commit f262fb4

Please sign in to comment.