mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
chore: don't require an organization to read starter templates (#14190)
This commit is contained in:
committed by
GitHub
parent
fab196043e
commit
ff785588fe
Generated
+29
@@ -3000,6 +3000,7 @@ const docTemplate = `{
|
||||
],
|
||||
"summary": "Get template examples by organization",
|
||||
"operationId": "get-template-examples-by-organization",
|
||||
"deprecated": true,
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
@@ -3421,6 +3422,34 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/templates/examples": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Templates"
|
||||
],
|
||||
"summary": "Get template examples",
|
||||
"operationId": "get-template-examples",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.TemplateExample"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/templates/{template}": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
||||
Generated
+25
@@ -2630,6 +2630,7 @@
|
||||
"tags": ["Templates"],
|
||||
"summary": "Get template examples by organization",
|
||||
"operationId": "get-template-examples-by-organization",
|
||||
"deprecated": true,
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
@@ -3005,6 +3006,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/templates/examples": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Templates"],
|
||||
"summary": "Get template examples",
|
||||
"operationId": "get-template-examples",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.TemplateExample"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/templates/{template}": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
||||
+2
-1
@@ -871,7 +871,7 @@ func New(options *Options) *API {
|
||||
r.Route("/templates", func(r chi.Router) {
|
||||
r.Post("/", api.postTemplateByOrganization)
|
||||
r.Get("/", api.templatesByOrganization())
|
||||
r.Get("/examples", api.templateExamples)
|
||||
r.Get("/examples", api.templateExamplesByOrganization)
|
||||
r.Route("/{templatename}", func(r chi.Router) {
|
||||
r.Get("/", api.templateByOrganizationAndName)
|
||||
r.Route("/versions/{templateversionname}", func(r chi.Router) {
|
||||
@@ -915,6 +915,7 @@ func New(options *Options) *API {
|
||||
apiKeyMiddleware,
|
||||
)
|
||||
r.Get("/", api.fetchTemplates(nil))
|
||||
r.Get("/examples", api.templateExamples)
|
||||
r.Route("/{template}", func(r chi.Router) {
|
||||
r.Use(
|
||||
httpmw.ExtractTemplateParam(options.Database),
|
||||
|
||||
+29
-1
@@ -821,7 +821,8 @@ func (api *API) templateDAUs(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Param organization path string true "Organization ID" format(uuid)
|
||||
// @Success 200 {array} codersdk.TemplateExample
|
||||
// @Router /organizations/{organization}/templates/examples [get]
|
||||
func (api *API) templateExamples(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Deprecated Use /templates/examples instead
|
||||
func (api *API) templateExamplesByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
organization = httpmw.OrganizationParam(r)
|
||||
@@ -844,6 +845,33 @@ func (api *API) templateExamples(rw http.ResponseWriter, r *http.Request) {
|
||||
httpapi.Write(ctx, rw, http.StatusOK, ex)
|
||||
}
|
||||
|
||||
// @Summary Get template examples
|
||||
// @ID get-template-examples
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Templates
|
||||
// @Success 200 {array} codersdk.TemplateExample
|
||||
// @Router /templates/examples [get]
|
||||
func (api *API) templateExamples(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
if !api.Authorize(r, policy.ActionRead, rbac.ResourceTemplate.AnyOrganization()) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
||||
ex, err := examples.List()
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching examples.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, ex)
|
||||
}
|
||||
|
||||
func (api *API) convertTemplates(templates []database.Template) []codersdk.Template {
|
||||
apiTemplates := make([]codersdk.Template, 0, len(templates))
|
||||
|
||||
|
||||
@@ -1097,17 +1097,17 @@ func TestPreviousTemplateVersion(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestTemplateExamples(t *testing.T) {
|
||||
func TestStarterTemplates(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
ex, err := client.TemplateExamples(ctx, user.OrganizationID)
|
||||
ex, err := client.StarterTemplates(ctx)
|
||||
require.NoError(t, err)
|
||||
ls, err := examples.List()
|
||||
require.NoError(t, err)
|
||||
|
||||
+10
-3
@@ -472,9 +472,16 @@ type AgentStatsReportResponse struct {
|
||||
TxBytes int64 `json:"tx_bytes"`
|
||||
}
|
||||
|
||||
// TemplateExamples lists example templates embedded in coder.
|
||||
func (c *Client) TemplateExamples(ctx context.Context, organizationID uuid.UUID) ([]TemplateExample, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/templates/examples", organizationID), nil)
|
||||
// TemplateExamples lists example templates available in Coder.
|
||||
//
|
||||
// Deprecated: Use StarterTemplates instead.
|
||||
func (c *Client) TemplateExamples(ctx context.Context, _ uuid.UUID) ([]TemplateExample, error) {
|
||||
return c.StarterTemplates(ctx)
|
||||
}
|
||||
|
||||
// StarterTemplates lists example templates available in Coder.
|
||||
func (c *Client) StarterTemplates(ctx context.Context) ([]TemplateExample, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, "/api/v2/templates/examples", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Generated
+54
@@ -761,6 +761,60 @@ Status Code **200**
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get template examples
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X GET http://coder-server:8080/api/v2/templates/examples \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`GET /templates/examples`
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"description": "string",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"markdown": "string",
|
||||
"name": "string",
|
||||
"tags": ["string"],
|
||||
"url": "string"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------------- |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.TemplateExample](schemas.md#codersdktemplateexample) |
|
||||
|
||||
<h3 id="get-template-examples-responseschema">Response Schema</h3>
|
||||
|
||||
Status Code **200**
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| --------------- | ------------ | -------- | ------------ | ----------- |
|
||||
| `[array item]` | array | false | | |
|
||||
| `» description` | string | false | | |
|
||||
| `» icon` | string | false | | |
|
||||
| `» id` | string(uuid) | false | | |
|
||||
| `» markdown` | string | false | | |
|
||||
| `» name` | string | false | | |
|
||||
| `» tags` | array | false | | |
|
||||
| `» url` | string | false | | |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get template metadata by ID
|
||||
|
||||
### Code samples
|
||||
|
||||
+3
-7
@@ -483,7 +483,7 @@ class ApiMethods {
|
||||
};
|
||||
|
||||
deleteToken = async (keyId: string): Promise<void> => {
|
||||
await this.axios.delete("/api/v2/users/me/keys/" + keyId);
|
||||
await this.axios.delete(`/api/v2/users/me/keys/${keyId}`);
|
||||
};
|
||||
|
||||
createToken = async (
|
||||
@@ -1754,12 +1754,8 @@ class ApiMethods {
|
||||
/**
|
||||
* @param organization Can be the organization's ID or name
|
||||
*/
|
||||
getTemplateExamples = async (
|
||||
organization: string,
|
||||
): Promise<TypesGen.TemplateExample[]> => {
|
||||
const response = await this.axios.get(
|
||||
`/api/v2/organizations/${organization}/templates/examples`,
|
||||
);
|
||||
getTemplateExamples = async (): Promise<TypesGen.TemplateExample[]> => {
|
||||
const response = await this.axios.get(`/api/v2/templates/examples`);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
@@ -112,13 +112,10 @@ export const setGroupRole = (
|
||||
};
|
||||
};
|
||||
|
||||
export const templateExamples = (organizationId: string) => {
|
||||
export const templateExamples = () => {
|
||||
return {
|
||||
queryKey: [
|
||||
...getTemplatesByOrganizationQueryKey(organizationId),
|
||||
"examples",
|
||||
],
|
||||
queryFn: () => API.getTemplateExamples(organizationId),
|
||||
queryKey: ["templates", "examples"],
|
||||
queryFn: () => API.getTemplateExamples(),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export const ImportStarterTemplateView: FC<CreateTemplatePageViewProps> = ({
|
||||
const { multiple_organizations: organizationsEnabled } =
|
||||
useFeatureVisibility();
|
||||
const [searchParams] = useSearchParams();
|
||||
const templateExamplesQuery = useQuery(templateExamples("default"));
|
||||
const templateExamplesQuery = useQuery(templateExamples());
|
||||
const templateExample = templateExamplesQuery.data?.find(
|
||||
(e) => e.id === searchParams.get("exampleId")!,
|
||||
);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { StarterTemplatesPageView } from "./StarterTemplatesPageView";
|
||||
|
||||
const CreateTemplatesGalleryPage: FC = () => {
|
||||
const { experiments } = useDashboard();
|
||||
const templateExamplesQuery = useQuery(templateExamples("default"));
|
||||
const templateExamplesQuery = useQuery(templateExamples());
|
||||
const starterTemplatesByTag = templateExamplesQuery.data
|
||||
? // Currently, the scratch template should not be displayed on the starter templates page.
|
||||
getTemplatesByTag(removeScratchExample(templateExamplesQuery.data))
|
||||
|
||||
@@ -8,7 +8,7 @@ import { StarterTemplatePageView } from "./StarterTemplatePageView";
|
||||
|
||||
const StarterTemplatePage: FC = () => {
|
||||
const { exampleId } = useParams() as { exampleId: string };
|
||||
const templateExamplesQuery = useQuery(templateExamples("default"));
|
||||
const templateExamplesQuery = useQuery(templateExamples());
|
||||
const starterTemplate = templateExamplesQuery.data?.find(
|
||||
(example) => example.id === exampleId,
|
||||
);
|
||||
|
||||
@@ -11,7 +11,7 @@ export const TemplatesPage: FC = () => {
|
||||
|
||||
const templatesQuery = useQuery(templates());
|
||||
const examplesQuery = useQuery({
|
||||
...templateExamples("default"),
|
||||
...templateExamples(),
|
||||
enabled: permissions.createTemplates,
|
||||
});
|
||||
const error = templatesQuery.error || examplesQuery.error;
|
||||
|
||||
@@ -47,9 +47,6 @@ export const handlers = [
|
||||
http.get("/api/v2/organizations/:organizationId", () => {
|
||||
return HttpResponse.json(M.MockOrganization);
|
||||
}),
|
||||
http.get("/api/v2/organizations/:organizationId/templates/examples", () => {
|
||||
return HttpResponse.json([M.MockTemplateExample, M.MockTemplateExample2]);
|
||||
}),
|
||||
http.get(
|
||||
"/api/v2/organizations/:organizationId/templates/:templateId",
|
||||
() => {
|
||||
@@ -81,6 +78,9 @@ export const handlers = [
|
||||
),
|
||||
|
||||
// templates
|
||||
http.get("/api/v2/templates/examples", () => {
|
||||
return HttpResponse.json([M.MockTemplateExample, M.MockTemplateExample2]);
|
||||
}),
|
||||
http.get("/api/v2/templates/:templateId", () => {
|
||||
return HttpResponse.json(M.MockTemplate);
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user