POC/Provisioning: update generator (#98027)

* Add source

* Update filters

* Exclude all namespace endpoints

* Remove watch endpoints

* Exclude collection endpoint

* Update arg names

* Add parser script

* Update spec

* Update @rtk-query/codegen-openapi

* Update params for list endpoints

* Use new hooks and types in hooks

* alternative operation ID

* alternative operation ID

* alt operation

* alt operation

* Move some transformations to BE

* Update endpoints

* Update types after generation

* Remove spec and generated endpoints

* Add tags automatically

* Update pkg/services/apiserver/builder/helper.go

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>

* add static openapi

* Use generated spec

---------

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
Alex Khomenko
2024-12-16 16:27:31 +02:00
committed by GitHub
co-authored by Ryan McKinley
parent 2f4f727758
commit dfb7d5110e
24 changed files with 6446 additions and 343 deletions
+2 -2
View File
@@ -63,7 +63,7 @@
"plugin:build:commit": "nx run-many -t build:commit --projects='tag:scope:plugin'",
"plugin:build:dev": "nx run-many -t dev --projects='tag:scope:plugin' --maxParallel=100",
"generate-icons": "nx run grafana-icons:generate",
"generate-apis": "rtk-query-codegen-openapi ./scripts/generate-rtk-apis.ts"
"generate-apis": "rtk-query-codegen-openapi ./scripts/generate-rtk-apis.ts && ts-node scripts/parse-generated-api.ts"
},
"grafana": {
"whatsNewUrl": "https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-v11-4/",
@@ -90,7 +90,7 @@
"@react-types/overlays": "3.8.11",
"@react-types/shared": "3.26.0",
"@rsdoctor/webpack-plugin": "^0.4.6",
"@rtk-query/codegen-openapi": "^1.2.0",
"@rtk-query/codegen-openapi": "^2.0.0",
"@rtsao/plugin-proposal-class-properties": "7.0.1-patch.1",
"@stylistic/eslint-plugin-ts": "^2.9.0",
"@swc/core": "1.10.1",
+96 -2
View File
@@ -2,9 +2,14 @@ package builder
import (
"context"
"encoding/csv"
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"regexp"
"strings"
"time"
"github.com/prometheus/client_golang/prometheus"
@@ -117,15 +122,104 @@ func SetupConfig(
// Add the custom routes to service discovery
serverConfig.OpenAPIV3Config.PostProcessSpec = getOpenAPIPostProcessor(buildVersion, builders)
serverConfig.OpenAPIV3Config.GetOperationIDAndTagsFromRoute = func(r common.Route) (string, []string, error) {
meta := r.Metadata()
kind := ""
action := ""
sub := ""
tags := []string{}
prop, ok := r.Metadata()["x-kubernetes-group-version-kind"]
prop, ok := meta["x-kubernetes-group-version-kind"]
if ok {
gvk, ok := prop.(metav1.GroupVersionKind)
if ok && gvk.Kind != "" {
kind = gvk.Kind
tags = append(tags, gvk.Kind)
}
}
return r.OperationName(), tags, nil
prop, ok = meta["x-kubernetes-action"]
if ok {
action = fmt.Sprintf("%v", prop)
}
isNew := false
if _, err := os.Stat("test.csv"); errors.Is(err, os.ErrNotExist) {
isNew = true
}
if action == "connect" {
idx := strings.LastIndex(r.Path(), "/{name}/")
if idx > 0 {
sub = r.Path()[(idx + len("/{name}/")):]
}
}
operationAlt := r.OperationName()
if action != "" {
if action == "connect" {
idx := strings.Index(r.OperationName(), "Namespaced")
if idx > 0 {
operationAlt = strings.ToLower(r.Method()) +
r.OperationName()[idx:]
}
}
}
operationAlt = strings.ReplaceAll(operationAlt, "Namespaced", "")
if strings.HasPrefix(operationAlt, "post") {
operationAlt = "create" + operationAlt[len("post"):]
} else if strings.HasPrefix(operationAlt, "read") {
operationAlt = "get" + operationAlt[len("read"):]
} else if strings.HasPrefix(operationAlt, "patch") {
operationAlt = "update" + operationAlt[len("patch"):]
}
// Audit our options here
if false {
// Safe to ignore G304 -- this will be removed before merging to main, and just helps audit the conversion
// nolint:gosec
f, err := os.OpenFile("test.csv", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
fmt.Printf("ERROR: %s\n", err)
} else {
metastr, _ := json.Marshal(meta)
prop, ok = meta["x-kubernetes-group-version-kind"]
if ok {
gvk, ok := prop.(metav1.GroupVersionKind)
if ok {
kind = gvk.Kind
}
}
w := csv.NewWriter(f)
if isNew {
w.Write([]string{
"#Path",
"Method",
"action",
"kind",
"sub",
"OperationName",
"OperationNameAlt",
"Description",
"metadata",
})
}
w.Write([]string{
r.Path(),
r.Method(),
action,
kind,
sub,
r.OperationName(),
operationAlt,
r.Description(),
string(metastr),
})
w.Flush()
}
}
return operationAlt, tags, nil
}
// Set the swagger build versions
+41
View File
@@ -8,10 +8,12 @@ import (
"io"
"net/http"
"os"
"path/filepath"
"strconv"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -168,6 +170,45 @@ func (c *K8sTestHelper) AsStatusError(err error) *errors.StatusError {
return statusError
}
func (c *K8sTestHelper) VerifyStaticOpenAPISpec(gv schema.GroupVersion, fpath string) {
c.t.Helper()
path := fmt.Sprintf("/openapi/v3/apis/%s/%s", gv.Group, gv.Version)
rsp := DoRequest(c, RequestParams{
Method: http.MethodGet,
Path: path,
User: c.Org1.Admin,
}, &AnyResource{})
require.NotNil(c.t, rsp.Response)
require.Equal(c.t, 200, rsp.Response.StatusCode)
var prettyJSON bytes.Buffer
err := json.Indent(&prettyJSON, rsp.Body, "", " ")
require.NoError(c.t, err)
pretty := prettyJSON.String()
// nolint:gosec
// We can ignore the gosec G304 warning since this is a test and the function is only called with explicit paths
body, err := os.ReadFile(fpath)
if err == nil {
if diff := cmp.Diff(pretty, string(body)); diff != "" {
str := fmt.Sprintf("body mismatch (-want +got):\n%s\n", diff)
err = fmt.Errorf(str)
}
}
if err != nil {
e2 := os.WriteFile(fpath, []byte(pretty), 0644)
if e2 != nil {
c.t.Errorf("error writing file: %s", e2.Error())
}
abs, _ := filepath.Abs(fpath)
c.t.Errorf("openapi spec has changed: %s (%s)", err.Error(), abs)
c.t.Fail()
}
}
func (c *K8sResourceClient) SanitizeJSONList(v *unstructured.UnstructuredList, replaceMeta ...string) string {
c.t.Helper()
@@ -202,6 +202,11 @@ func TestIntegrationProvisioning(t *testing.T) {
}
]
}`, string(v1Disco))
// also verify the openapi
helper.VerifyStaticOpenAPISpec(schema.GroupVersion{
Group: "provisioning.grafana.app", Version: "v0alpha1",
}, "testdata/openapi.json")
})
t.Run("Check basic create and get", func(t *testing.T) {
File diff suppressed because it is too large Load Diff
@@ -20,8 +20,7 @@ import { AnnoKeyRepoName, AnnoKeyRepoPath } from 'app/features/apiserver/types';
import { DashboardMeta } from 'app/types';
import { RepositorySelect } from '../../provisioning/RepositorySelect';
import { useGetRepositoryQuery } from '../../provisioning/api';
import { RepositorySpec } from '../../provisioning/api/types';
import { useGetRepositoryQuery, RepositorySpec } from '../../provisioning/api';
import { PROVISIONING_URL } from '../../provisioning/constants';
import { useCreateOrUpdateRepositoryFile, usePullRequestParam } from '../../provisioning/hooks';
import { WorkflowOption } from '../../provisioning/types';
@@ -23,7 +23,10 @@ const injectedRtkApi = api.injectEndpoints({
getSnapshot: build.query<GetSnapshotApiResponse, GetSnapshotApiArg>({
query: (queryArg) => ({
url: `/cloudmigration/migration/${queryArg.uid}/snapshot/${queryArg.snapshotUid}`,
params: { resultPage: queryArg.resultPage, resultLimit: queryArg.resultLimit },
params: {
resultPage: queryArg.resultPage,
resultLimit: queryArg.resultLimit,
},
}),
}),
cancelSnapshot: build.mutation<CancelSnapshotApiResponse, CancelSnapshotApiArg>({
@@ -41,7 +44,11 @@ const injectedRtkApi = api.injectEndpoints({
getShapshotList: build.query<GetShapshotListApiResponse, GetShapshotListApiArg>({
query: (queryArg) => ({
url: `/cloudmigration/migration/${queryArg.uid}/snapshots`,
params: { page: queryArg.page, limit: queryArg.limit, sort: queryArg.sort },
params: {
page: queryArg.page,
limit: queryArg.limit,
sort: queryArg.sort,
},
}),
}),
getCloudMigrationToken: build.query<GetCloudMigrationTokenApiResponse, GetCloudMigrationTokenApiArg>({
@@ -95,14 +102,14 @@ export type GetSnapshotApiArg = {
/** UID of a snapshot */
snapshotUid: string;
};
export type CancelSnapshotApiResponse = /** status 200 (empty) */ void;
export type CancelSnapshotApiResponse = unknown;
export type CancelSnapshotApiArg = {
/** Session UID of a session */
uid: string;
/** UID of a snapshot */
snapshotUid: string;
};
export type UploadSnapshotApiResponse = /** status 200 (empty) */ void;
export type UploadSnapshotApiResponse = unknown;
export type UploadSnapshotApiArg = {
/** Session UID of a session */
uid: string;
@@ -124,7 +131,7 @@ export type GetCloudMigrationTokenApiResponse = /** status 200 (empty) */ GetAcc
export type GetCloudMigrationTokenApiArg = void;
export type CreateCloudMigrationTokenApiResponse = /** status 200 (empty) */ CreateAccessTokenResponseDto;
export type CreateCloudMigrationTokenApiArg = void;
export type DeleteCloudMigrationTokenApiResponse = /** status 204 (empty) */ void;
export type DeleteCloudMigrationTokenApiResponse = unknown;
export type DeleteCloudMigrationTokenApiArg = {
/** UID of a cloud migration token */
uid: string;
@@ -19,7 +19,7 @@ import {
import { FormPrompt } from 'app/core/components/FormPrompt/FormPrompt';
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
import { RepositoryResource, RepositorySpec } from './api/types';
import { Repository, RepositorySpec } from './api';
import { useCreateOrUpdateRepository } from './hooks';
import { RepositoryFormData } from './types';
import { dataToSpec, specToData } from './utils/data';
@@ -52,10 +52,10 @@ function getDefaultValues(repository?: RepositorySpec): RepositoryFormData {
}
export interface ConfigFormProps {
data?: RepositoryResource;
data?: Repository;
}
export function ConfigForm({ data }: ConfigFormProps) {
const [submitData, request] = useCreateOrUpdateRepository(data?.metadata.name);
const [submitData, request] = useCreateOrUpdateRepository(data?.metadata?.name);
const {
register,
handleSubmit,
@@ -21,7 +21,7 @@ export function DeleteRepositoryButton({ name }: { name: string }) {
}, [request.isSuccess]);
const onConfirm = useCallback(() => {
deleteRepository({ name });
deleteRepository({ name, body: {} });
}, [deleteRepository, name]);
return (
@@ -3,7 +3,7 @@ import { useParams } from 'react-router-dom-v5-compat';
import { Card, EmptyState, Spinner, Text, TextLink } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page';
import { useListRepositoryFileHistoryQuery, useGetRepositoryStatusQuery } from './api';
import { useGetRepositoryHistoryWithPathQuery, useGetRepositoryStatusQuery } from './api';
import { HistoryListResponse } from './api/types';
import { PROVISIONING_URL } from './constants';
@@ -12,7 +12,7 @@ export default function FileHistoryPage() {
const name = params['name'] ?? '';
const path = params['*'] ?? '';
const query = useGetRepositoryStatusQuery({ name });
const history = useListRepositoryFileHistoryQuery({ name, path });
const history = useGetRepositoryHistoryWithPathQuery({ name, path });
//@ts-expect-error TODO add error types
const notFound = query.isError && query.error?.status === 404;
@@ -21,7 +21,7 @@ export default function FileHistoryPage() {
navId="provisioning"
pageNav={{
text: `History: ${path}`, //,
subTitle: query.data?.spec.title ?? 'Repository',
subTitle: query.data?.spec?.title ?? 'Repository',
}}
>
<Page.Contents isLoading={false}>
@@ -31,6 +31,7 @@ export default function FileHistoryPage() {
<TextLink href={PROVISIONING_URL}>Back to repositories</TextLink>
</EmptyState>
) : (
//@ts-expect-error TODO fix history response types
<div>{history.data ? <HistoryView history={history.data} path={path} repo={name} /> : <Spinner />}</div>
)}
</Page.Contents>
@@ -19,8 +19,7 @@ import {
import { Page } from 'app/core/components/Page/Page';
import { useQueryParams } from 'app/core/hooks/useQueryParams';
import { useGetRepositoryFilesQuery, useGetRepositoryStatusQuery } from './api';
import { ResourceWrapper } from './api/types';
import { useGetRepositoryFilesWithPathQuery, useGetRepositoryStatusQuery, ResourceWrapper } from './api';
import { PROVISIONING_URL } from './constants';
export default function FileStatusPage() {
@@ -31,7 +30,7 @@ export default function FileStatusPage() {
const name = params['name'] ?? '';
const path = params['*'] ?? '';
const query = useGetRepositoryStatusQuery({ name });
const file = useGetRepositoryFilesQuery({ name, path, ref });
const file = useGetRepositoryFilesWithPathQuery({ name, path, ref });
//@ts-expect-error TODO add error types
const notFound = query.isError && query.error?.status === 404;
@@ -40,7 +39,7 @@ export default function FileStatusPage() {
navId="provisioning"
pageNav={{
text: `File: ${path} ${ref ? `(@${ref})` : ''}`,
subTitle: query.data?.spec.title ?? 'Repository',
subTitle: query.data?.spec?.title ?? 'Repository',
}}
>
<Page.Contents isLoading={false}>
@@ -141,11 +140,14 @@ function ResourceView({ wrap, repo, repoRef, tab }: Props) {
{tab === TabSelection.Lint && (
<div>
{wrap.lint ? (
wrap.lint.map((r, idx) => (
<Alert title={r.rule} severity={r.severity} key={`${r.rule}/${idx}`}>
{r.message}
</Alert>
))
wrap.lint.map((r, idx) => {
return (
//@ts-expect-error TODO fix lint response types
<Alert title={r.rule} severity={r.severity} key={`${r.rule}/${idx}`}>
{r.message}
</Alert>
);
})
) : (
<div>
<h3>No lint issues</h3>
@@ -4,11 +4,9 @@ import { useState } from 'react';
import { Card, EmptySearchResult, EmptyState, FilterInput, LinkButton, Stack, TextLink } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page';
import { Resource } from '../apiserver/types';
import { DeleteRepositoryButton } from './DeleteRepositoryButton';
import { SyncRepository } from './SyncRepository';
import { RepositorySpec } from './api/types';
import { Repository } from './api';
import { NEW_URL, PROVISIONING_URL } from './constants';
import { useRepositoryList } from './hooks';
@@ -23,7 +21,7 @@ export default function RepositoryListPage() {
);
}
function RepositoryListPageContent({ items }: { items?: Array<Resource<RepositorySpec>> }) {
function RepositoryListPageContent({ items }: { items?: Repository[] }) {
const [query, setQuery] = useState('');
if (!items?.length) {
return (
@@ -39,7 +37,7 @@ function RepositoryListPageContent({ items }: { items?: Array<Resource<Repositor
);
}
const filteredItems = items.filter((item) => item.metadata.name.includes(query));
const filteredItems = items.filter((item) => item.metadata?.name?.includes(query));
return (
<Stack direction={'column'} gap={3}>
@@ -52,20 +50,21 @@ function RepositoryListPageContent({ items }: { items?: Array<Resource<Repositor
<Stack direction={'column'}>
{!!filteredItems.length ? (
filteredItems.map((item) => {
const name = item.metadata?.name ?? '';
return (
<Card key={item.metadata.name} className={css({ alignItems: 'center' })}>
<Card key={item.metadata?.name} className={css({ alignItems: 'center' })}>
<Stack direction={'column'}>
<Card.Heading>
<TextLink href={`${PROVISIONING_URL}/${item.metadata.name}`}>{item.spec.title}</TextLink>
<TextLink href={`${PROVISIONING_URL}/${name}`}>{item.spec?.title}</TextLink>
</Card.Heading>
<Card.Meta>{item.spec.type}</Card.Meta>
<Card.Meta>{item.spec?.type}</Card.Meta>
</Stack>
<Stack>
<LinkButton variant="secondary" href={`${PROVISIONING_URL}/${item.metadata.name}/edit`}>
<LinkButton variant="secondary" href={`${PROVISIONING_URL}/${name}/edit`}>
Edit
</LinkButton>
<SyncRepository repository={item} />
<DeleteRepositoryButton name={item.metadata.name} />
<DeleteRepositoryButton name={name} />
</Stack>
</Card>
);
@@ -16,8 +16,8 @@ export function RepositorySelect(props: Pick<ComboboxBaseProps<string>, 'value'
placeholder={'Select a repository'}
options={
listQuery.data?.items?.map((repo) => ({
label: repo.spec.title,
value: repo.metadata.name,
label: repo.spec?.title,
value: repo.metadata?.name ?? '',
})) || []
}
/>
@@ -4,7 +4,6 @@ import { useParams } from 'react-router-dom-v5-compat';
import { SelectableValue, urlUtil } from '@grafana/data';
import {
List,
Alert,
Card,
CellProps,
@@ -24,13 +23,8 @@ import {
import { Page } from 'app/core/components/Page/Page';
import { useQueryParams } from 'app/core/hooks/useQueryParams';
import {
useGetRepositoryStatusQuery,
useListJobsQuery,
useListRepositoryFilesQuery,
useTestRepositoryQuery,
} from './api';
import { RepositoryResource, FileDetails, TestResponse } from './api/types';
import { useGetRepositoryStatusQuery, useListJobQuery, useGetRepositoryFilesQuery, Repository } from './api';
import { FileDetails } from './api/types';
import { PROVISIONING_URL } from './constants';
enum TabSelection {
@@ -60,7 +54,7 @@ export default function RepositoryStatusPage() {
<Page
navId="provisioning"
pageNav={{
text: query.data?.spec.title ?? 'Repository Status',
text: query.data?.spec?.title ?? 'Repository Status',
subTitle: 'Check the status of configured repository.',
}}
>
@@ -74,7 +68,7 @@ export default function RepositoryStatusPage() {
<>
{query.data ? (
<>
<ErrorView repo={query.data} />
{/*<ErrorView repo={query.data} />*/}
<TabsBar>
{tabInfo.map((t: SelectableValue) => (
<Tab
@@ -102,39 +96,39 @@ export default function RepositoryStatusPage() {
);
}
interface RepoProps {
repo: RepositoryResource;
repo: Repository;
}
function ErrorView({ repo }: RepoProps) {
const name = repo.metadata.name;
const status = useTestRepositoryQuery({ name });
if (status.isLoading) {
return (
<div>
<Spinner /> Testing configuration...
</div>
);
}
if (status.isError) {
let response = (status.error as any)?.data as TestResponse;
if (!response || !response.errors) {
return <Alert title="Error testing configuration" severity="error" />;
}
return (
<Alert title="Error testing configuration" severity="error">
<List items={response.errors} renderItem={(error) => <div>{error}</div>} />
</Alert>
);
}
return null; // don't show anything when it is OK?
}
// function ErrorView({ repo }: RepoProps) {
// const name = repo.metadata.name;
// const status = useTestRepositoryQuery({ name });
// if (status.isLoading) {
// return (
// <div>
// <Spinner /> Testing configuration...
// </div>
// );
// }
// if (status.isError) {
// let response = (status.error as any)?.data as TestResponse;
// if (!response || !response.errors) {
// return <Alert title="Error testing configuration" severity="error" />;
// }
//
// return (
// <Alert title="Error testing configuration" severity="error">
// <List items={response.errors} renderItem={(error) => <div>{error}</div>} />
// </Alert>
// );
// }
// return null; // don't show anything when it is OK?
// }
type Cell<T extends keyof FileDetails = keyof FileDetails> = CellProps<FileDetails, FileDetails[T]>;
function FilesView({ repo }: RepoProps) {
const name = repo.metadata.name;
const query = useListRepositoryFilesQuery({ name });
const name = repo.metadata?.name ?? '';
const query = useGetRepositoryFilesQuery({ name });
const [searchQuery, setSearchQuery] = useState('');
const data = [...(query.data?.files ?? [])].filter((file) =>
file.path.toLowerCase().includes(searchQuery.toLowerCase())
@@ -205,8 +199,8 @@ function FilesView({ repo }: RepoProps) {
}
function JobsView({ repo }: RepoProps) {
const name = repo.metadata.name;
const query = useListJobsQuery({
const name = repo.metadata?.name;
const query = useListJobQuery({
labelSelector: [
{
key: 'repository',
@@ -241,9 +235,9 @@ function JobsView({ repo }: RepoProps) {
<div>
{items.map((item) => {
return (
<Card key={item.metadata.resourceVersion}>
<Card key={item.metadata?.resourceVersion}>
<Card.Heading>
{item.spec.action} / {item.status?.state}
{item.spec?.action} / {item.status?.state}
</Card.Heading>
<Card.Description>
<span>{JSON.stringify(item.spec)}</span>
@@ -259,9 +253,9 @@ function JobsView({ repo }: RepoProps) {
function FolderView({ repo }: RepoProps) {
return (
<div>
<h2>TODO, show folder: {repo.metadata.name}</h2>
<h2>TODO, show folder: {repo.metadata?.name}</h2>
<br />
<a href={`/dashboards/f/${repo.spec.folder} /`}>{repo.spec.folder} </a>
<a href={`/dashboards/f/${repo.spec?.folder} /`}>{repo.spec?.folder} </a>
</div>
);
}
@@ -269,9 +263,9 @@ function FolderView({ repo }: RepoProps) {
function SettingsView({ repo }: RepoProps) {
return (
<div>
<h2>TODO, show settings inline???: {repo.metadata.name}</h2>
<h2>TODO, show settings inline???: {repo.metadata?.name}</h2>
<LinkButton href={`${PROVISIONING_URL}/${repo.metadata.name}/edit`}>Edit</LinkButton>
<LinkButton href={`${PROVISIONING_URL}/${repo.metadata?.name}/edit`}>Edit</LinkButton>
</div>
);
}
@@ -6,14 +6,12 @@ import { AppEvents } from '@grafana/data';
import { config, getAppEvents, getBackendSrv } from '@grafana/runtime';
import { Button, ConfirmModal } from '@grafana/ui';
import { Resource } from '../apiserver/types';
import { Loader } from '../plugins/admin/components/Loader';
import { useCreateRepositorySyncMutation, useListRepositoryQuery } from './api';
import { RepositorySpec } from './api/types';
import { Repository, useCreateRepositorySyncMutation, useListRepositoryQuery } from './api';
interface Props {
repository: Resource<RepositorySpec>;
repository: Repository;
}
export function SyncRepository({ repository }: Props) {
@@ -39,5 +39,4 @@ export const baseAPI = createApi({
baseURL: BASE_URL,
}),
endpoints: () => ({}),
tagTypes: ['RepositoryList', 'JobList'],
});
+642 -222
View File
@@ -1,238 +1,658 @@
import { Resource } from 'app/features/apiserver/types';
import { parseListOptionsSelector } from '../../apiserver/client';
import { baseAPI as api } from './baseAPI';
import {
RepositoryList,
RepositoryResource,
RequestArg,
UpdateRequestArg,
RepositoryForCreate,
ResourceWrapper,
FileOperationArg,
GetFileArg,
ListFilesApiResponse,
HistoryListResponse,
TestResponse,
RepositorySpec,
JobList,
JobResource,
ListApiArg,
WebhookResponse,
HistoryItem,
GetRequestArg,
} from './types';
const BASE_PATH = '/repositories';
const injectedRtkApi = api.injectEndpoints({
endpoints: (build) => ({
listJobs: build.query<JobList, ListApiArg | void>({
query: (queryArg) => {
return {
export const addTagTypes = ['Job', 'Repository'] as const;
const injectedRtkApi = api
.enhanceEndpoints({
addTagTypes,
})
.injectEndpoints({
endpoints: (build) => ({
listJob: build.query<ListJobResponse, ListJobArg | void>({
query: (queryArg) => ({
url: `/jobs`,
params: getListParams(queryArg),
};
},
providesTags: ['JobList'],
}),
getJob: build.query<JobResource, RequestArg>({
query: (queryArg) => ({
url: `/jobs/${queryArg.name}`,
}),
providesTags: ['Job'],
}),
getJob: build.query<GetJobResponse, GetJobArg>({
query: (queryArg) => ({
url: `/jobs/${queryArg.name}`,
params: {
pretty: queryArg.pretty,
},
}),
providesTags: ['Job'],
}),
listRepository: build.query<ListRepositoryResponse, ListRepositoryArg | void>({
query: (queryArg) => ({
url: `/repositories`,
}),
providesTags: ['Repository'],
}),
createRepository: build.mutation<CreateRepositoryResponse, CreateRepositoryArg>({
query: (queryArg) => ({
url: `/repositories`,
method: 'POST',
body: queryArg.body,
params: {
pretty: queryArg.pretty,
dryRun: queryArg.dryRun,
fieldManager: queryArg.fieldManager,
fieldValidation: queryArg.fieldValidation,
},
}),
invalidatesTags: ['Repository'],
}),
getRepository: build.query<GetRepositoryResponse, GetRepositoryArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}`,
params: {
pretty: queryArg.pretty,
},
}),
providesTags: ['Repository'],
}),
replaceRepository: build.mutation<ReplaceRepositoryResponse, ReplaceRepositoryArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}`,
method: 'PUT',
body: queryArg.body,
params: {
pretty: queryArg.pretty,
dryRun: queryArg.dryRun,
fieldManager: queryArg.fieldManager,
fieldValidation: queryArg.fieldValidation,
},
}),
invalidatesTags: ['Repository'],
}),
deleteRepository: build.mutation<DeleteRepositoryResponse, DeleteRepositoryArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}`,
method: 'DELETE',
body: queryArg.body,
params: {
pretty: queryArg.pretty,
dryRun: queryArg.dryRun,
gracePeriodSeconds: queryArg.gracePeriodSeconds,
orphanDependents: queryArg.orphanDependents,
propagationPolicy: queryArg.propagationPolicy,
},
}),
invalidatesTags: ['Repository'],
}),
updateRepository: build.mutation<UpdateRepositoryResponse, UpdateRepositoryArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}`,
method: 'PATCH',
headers: { 'Content-Type': 'application/merge-patch+json' },
body: queryArg.body,
params: {
pretty: queryArg.pretty,
dryRun: queryArg.dryRun,
fieldManager: queryArg.fieldManager,
fieldValidation: queryArg.fieldValidation,
force: queryArg.force,
},
}),
invalidatesTags: ['Repository'],
}),
createRepositoryExport: build.mutation<CreateRepositoryExportResponse, CreateRepositoryExportArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/export`,
method: 'POST',
}),
invalidatesTags: ['Repository'],
}),
getRepositoryFiles: build.query<GetRepositoryFilesResponse, GetRepositoryFilesArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/files/`,
params: {
ref: queryArg.ref,
},
}),
providesTags: ['Repository'],
}),
getRepositoryFilesWithPath: build.query<GetRepositoryFilesWithPathResponse, GetRepositoryFilesWithPathArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/files/${queryArg.path}`,
params: {
ref: queryArg.ref,
},
}),
providesTags: ['Repository'],
}),
putRepositoryFilesWithPath: build.mutation<PutRepositoryFilesWithPathResponse, PutRepositoryFilesWithPathArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/files/${queryArg.path}`,
method: 'PUT',
body: queryArg.body,
params: {
ref: queryArg.ref,
message: queryArg.message,
},
}),
invalidatesTags: ['Repository'],
}),
createRepositoryFilesWithPath: build.mutation<
CreateRepositoryFilesWithPathResponse,
CreateRepositoryFilesWithPathArg
>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/files/${queryArg.path}`,
method: 'POST',
body: queryArg.body,
params: {
ref: queryArg.ref,
message: queryArg.message,
},
}),
invalidatesTags: ['Repository'],
}),
deleteRepositoryFilesWithPath: build.mutation<
DeleteRepositoryFilesWithPathResponse,
DeleteRepositoryFilesWithPathArg
>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/files/${queryArg.path}`,
method: 'DELETE',
params: {
ref: queryArg.ref,
message: queryArg.message,
},
}),
invalidatesTags: ['Repository'],
}),
getRepositoryHistory: build.query<GetRepositoryHistoryResponse, GetRepositoryHistoryArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/history`,
params: {
ref: queryArg.ref,
},
}),
providesTags: ['Repository'],
}),
getRepositoryHistoryWithPath: build.query<GetRepositoryHistoryWithPathResponse, GetRepositoryHistoryWithPathArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/history/${queryArg.path}`,
params: {
ref: queryArg.ref,
},
}),
providesTags: ['Repository'],
}),
getRepositoryStatus: build.query<GetRepositoryStatusResponse, GetRepositoryStatusArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/status`,
params: {
pretty: queryArg.pretty,
},
}),
providesTags: ['Repository'],
}),
replaceRepositoryStatus: build.mutation<ReplaceRepositoryStatusResponse, ReplaceRepositoryStatusArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/status`,
method: 'PUT',
body: queryArg.body,
params: {
pretty: queryArg.pretty,
dryRun: queryArg.dryRun,
fieldManager: queryArg.fieldManager,
fieldValidation: queryArg.fieldValidation,
},
}),
invalidatesTags: ['Repository'],
}),
updateRepositoryStatus: build.mutation<UpdateRepositoryStatusResponse, UpdateRepositoryStatusArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/status`,
method: 'PATCH',
headers: { 'Content-Type': 'application/merge-patch+json' },
body: queryArg.body,
params: {
pretty: queryArg.pretty,
dryRun: queryArg.dryRun,
fieldManager: queryArg.fieldManager,
fieldValidation: queryArg.fieldValidation,
force: queryArg.force,
},
}),
invalidatesTags: ['Repository'],
}),
createRepositorySync: build.mutation<CreateRepositorySyncResponse, CreateRepositorySyncArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/sync`,
method: 'POST',
}),
invalidatesTags: ['Repository'],
}),
createRepositoryTest: build.mutation<CreateRepositoryTestResponse, CreateRepositoryTestArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/test`,
method: 'POST',
body: queryArg.body,
}),
invalidatesTags: ['Repository'],
}),
getRepositoryWebhook: build.query<GetRepositoryWebhookResponse, GetRepositoryWebhookArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/webhook`,
}),
providesTags: ['Repository'],
}),
createRepositoryWebhook: build.mutation<CreateRepositoryWebhookResponse, CreateRepositoryWebhookArg>({
query: (queryArg) => ({
url: `/repositories/${queryArg.name}/webhook`,
method: 'POST',
}),
invalidatesTags: ['Repository'],
}),
}),
listRepository: build.query<RepositoryList, ListApiArg | void>({
query: (params) => ({
url: `${BASE_PATH}`,
params: getListParams(params),
}),
providesTags: ['RepositoryList'],
}),
createRepository: build.mutation<void, RepositoryForCreate>({
query: (body) => ({
url: BASE_PATH,
method: 'POST',
body,
}),
invalidatesTags: ['RepositoryList'],
}),
updateRepository: build.mutation<void, UpdateRequestArg>({
query: ({ name, body }) => ({
url: `${BASE_PATH}/${name}`,
method: 'PUT',
body,
}),
}),
deleteRepository: build.mutation<void, RequestArg>({
query: ({ name }) => ({
url: `${BASE_PATH}/${name}`,
method: 'DELETE',
}),
invalidatesTags: ['RepositoryList'],
}),
patchRepository: build.mutation<void, UpdateRequestArg>({
query: ({ name, body }) => ({
url: `${BASE_PATH}/${name}`,
method: 'PATCH',
headers: { 'Content-Type': 'application/merge-patch+json' },
body,
}),
invalidatesTags: ['RepositoryList'],
}),
getRepository: build.query<RepositoryResource, RequestArg>({
query: (queryArg) => ({
url: `${BASE_PATH}/${queryArg.name}`,
}),
}),
createRepositoryExport: build.mutation<ResourceWrapper, RequestArg>({
query: ({ name }) => ({
url: `${BASE_PATH}/${name}/export`,
method: 'POST',
}),
}),
getRepositoryFiles: build.query<ResourceWrapper, GetFileArg>({
query: ({ name, path, ref }) => ({
url: `${BASE_PATH}/${name}/files/${path}`,
params: { ref },
}),
}),
listRepositoryFiles: build.query<ListFilesApiResponse, { name: string; ref?: string }>({
query: ({ name, ref }) => ({
url: `${BASE_PATH}/${name}/files/`,
params: { ref },
}),
}),
listRepositoryFileHistory: build.query<HistoryListResponse, { name: string; path: string; ref?: string }>({
query: ({ name, ref, path }) => ({
url: `${BASE_PATH}/${name}/history/${path}`,
params: { ref },
}),
}),
updateRepositoryFiles: build.mutation<ResourceWrapper, FileOperationArg>({
query: ({ name, path, body, ref, message }) => ({
url: `${BASE_PATH}/${name}/files/${path}`,
method: 'PUT',
body,
params: { ref, message },
}),
}),
createRepositoryFiles: build.mutation<ResourceWrapper, FileOperationArg>({
query: ({ name, path, body, ref, message }) => ({
url: `${BASE_PATH}/${name}/files/${path}`,
method: 'POST',
body,
params: { ref, message },
}),
}),
deleteRepositoryFiles: build.mutation<
ResourceWrapper,
{ name: string; path: string; ref?: string; message?: string }
>({
query: ({ name, path, ref, message }) => ({
url: `${BASE_PATH}/${name}/files/${path}`,
method: 'DELETE',
params: { ref, message },
}),
}),
listRepositoryHistory: build.query<HistoryListResponse, RequestArg>({
query: (queryArg) => ({
url: `${BASE_PATH}/${queryArg.name}/history`,
params: { ref: queryArg.ref },
}),
}),
getRepositoryHistory: build.query<HistoryItem, GetRequestArg>({
query: (queryArg) => ({
url: `${BASE_PATH}/${queryArg.name}/history/${queryArg.path}`,
params: { ref: queryArg.ref },
}),
}),
createRepositorySync: build.mutation<ResourceWrapper, { name: string }>({
query: ({ name }) => ({
url: `${BASE_PATH}/${name}/sync`,
method: 'POST',
}),
}),
getRepositoryStatus: build.query<RepositoryResource, RequestArg>({
query: ({ name }) => ({
url: `${BASE_PATH}/${name}/status`,
}),
}),
updateRepositoryStatus: build.mutation<RepositoryResource, UpdateRequestArg>({
query: ({ name, body }) => ({
url: `${BASE_PATH}/${name}/status`,
method: 'PUT',
body,
}),
}),
patchRepositoryStatus: build.mutation<RepositoryResource, UpdateRequestArg>({
query: ({ name, body }) => ({
url: `${BASE_PATH}/${name}/status`,
method: 'PATCH',
headers: { 'Content-Type': 'application/merge-patch+json' },
body,
}),
}),
testRepository: build.query<TestResponse, { name: string }>({
query: ({ name }) => ({
url: `${BASE_PATH}/${name}/test`,
method: 'POST', // tests the existing configuration
}),
}),
testRepositoryConfig: build.mutation<TestResponse, Resource<RepositorySpec>>({
query: ({ metadata }) => ({
url: `${BASE_PATH}/${metadata.name ?? 'new'}/test`,
method: 'POST',
}),
}),
getRepositoryWebhook: build.query<WebhookResponse, RequestArg>({
query: (queryArg) => ({
url: `${BASE_PATH}/${queryArg.name}/webhook`,
}),
}),
postRepositoryWebhook: build.mutation<WebhookResponse, RequestArg>({
query: (queryArg) => ({
url: `${BASE_PATH}/${queryArg.name}/webhook`,
method: 'POST',
}),
}),
}),
overrideExisting: false,
});
overrideExisting: false,
});
export { injectedRtkApi as generatedAPI };
export type ListJobResponse = JobList;
export type ListJobArg = {};
export type GetJobResponse = Job;
export type GetJobArg = {
name: string;
pretty?: string;
};
export type ListRepositoryResponse = RepositoryList;
export type ListRepositoryArg = {};
export type CreateRepositoryResponse = Repository;
export type CreateRepositoryArg = {
pretty?: string;
dryRun?: string;
fieldManager?: string;
fieldValidation?: string;
body: Repository;
};
export type GetRepositoryResponse = Repository;
export type GetRepositoryArg = {
name: string;
pretty?: string;
};
export type ReplaceRepositoryResponse = Repository;
export type ReplaceRepositoryArg = {
name: string;
pretty?: string;
dryRun?: string;
fieldManager?: string;
fieldValidation?: string;
body: Repository;
};
export type DeleteRepositoryResponse = Status;
export type DeleteRepositoryArg = {
name: string;
pretty?: string;
dryRun?: string;
gracePeriodSeconds?: number;
orphanDependents?: boolean;
propagationPolicy?: string;
body: DeleteOptions;
};
export type UpdateRepositoryResponse = Repository;
export type UpdateRepositoryArg = {
name: string;
pretty?: string;
dryRun?: string;
fieldManager?: string;
fieldValidation?: string;
force?: boolean;
body: Patch;
};
export type CreateRepositoryExportResponse = Job;
export type CreateRepositoryExportArg = {
name: string;
};
export type GetRepositoryFilesResponse = {
apiVersion?: string;
files?: any[];
kind?: string;
metadata?: any;
};
export type GetRepositoryFilesArg = {
name: string;
ref?: string;
};
export type GetRepositoryFilesWithPathResponse = ResourceWrapper;
export type GetRepositoryFilesWithPathArg = {
name: string;
path: string;
ref?: string;
};
export type PutRepositoryFilesWithPathResponse = ResourceWrapper;
export type PutRepositoryFilesWithPathArg = {
name: string;
path: string;
ref?: string;
message?: string;
body: {
[key: string]: any;
};
};
export type CreateRepositoryFilesWithPathResponse = ResourceWrapper;
export type CreateRepositoryFilesWithPathArg = {
name: string;
path: string;
ref?: string;
message?: string;
body: {
[key: string]: any;
};
};
export type DeleteRepositoryFilesWithPathResponse = ResourceWrapper;
export type DeleteRepositoryFilesWithPathArg = {
name: string;
path: string;
ref?: string;
message?: string;
};
export type GetRepositoryHistoryResponse = string;
export type GetRepositoryHistoryArg = {
name: string;
ref?: string;
};
export type GetRepositoryHistoryWithPathResponse = string;
export type GetRepositoryHistoryWithPathArg = {
name: string;
path: string;
ref?: string;
};
export type GetRepositoryStatusResponse = Repository;
export type GetRepositoryStatusArg = {
name: string;
pretty?: string;
};
export type ReplaceRepositoryStatusResponse = Repository;
export type ReplaceRepositoryStatusArg = {
name: string;
pretty?: string;
dryRun?: string;
fieldManager?: string;
fieldValidation?: string;
body: Repository;
};
export type UpdateRepositoryStatusResponse = Repository;
export type UpdateRepositoryStatusArg = {
name: string;
pretty?: string;
dryRun?: string;
fieldManager?: string;
fieldValidation?: string;
force?: boolean;
body: Patch;
};
export type CreateRepositorySyncResponse = Job;
export type CreateRepositorySyncArg = {
name: string;
};
export type CreateRepositoryTestResponse = TestResults;
export type CreateRepositoryTestArg = {
name: string;
body: {
apiVersion?: string;
kind?: string;
metadata?: any;
spec?: any;
status?: any;
};
};
export type GetRepositoryWebhookResponse = WebhookResponse;
export type GetRepositoryWebhookArg = {
name: string;
};
export type CreateRepositoryWebhookResponse = WebhookResponse;
export type CreateRepositoryWebhookArg = {
name: string;
};
export type Time = string;
export type FieldsV1 = object;
export type ManagedFieldsEntry = {
apiVersion?: string;
fieldsType?: string;
fieldsV1?: FieldsV1;
manager?: string;
operation?: string;
subresource?: string;
time?: Time;
};
export type OwnerReference = {
apiVersion: string;
blockOwnerDeletion?: boolean;
controller?: boolean;
kind: string;
name: string;
uid: string;
};
export type ObjectMeta = {
annotations?: {
[key: string]: string;
};
creationTimestamp?: Time;
deletionGracePeriodSeconds?: number;
deletionTimestamp?: Time;
finalizers?: string[];
generateName?: string;
generation?: number;
labels?: {
[key: string]: string;
};
managedFields?: ManagedFieldsEntry[];
name?: string;
namespace?: string;
ownerReferences?: OwnerReference[];
resourceVersion?: string;
selfLink?: string;
uid?: string;
};
export type JobSpec = {
action: 'export' | 'pr' | 'sync';
hash?: string;
pr?: number;
ref?: string;
url?: string;
};
export type JobStatus = {
errors?: string[];
finished?: number;
message?: string;
started?: number;
state?: 'error' | 'pending' | 'success' | 'working';
};
export type Job = {
apiVersion?: string;
kind?: string;
metadata?: ObjectMeta;
spec?: JobSpec;
status?: JobStatus;
};
export type ListMeta = {
continue?: string;
remainingItemCount?: number;
resourceVersion?: string;
selfLink?: string;
};
export type JobList = {
apiVersion?: string;
items?: Job[];
kind?: string;
metadata?: ListMeta;
};
export type EditingOptions = {
create: boolean;
delete: boolean;
update: boolean;
};
export type GitHubRepositoryConfig = {
branch?: string;
branchWorkflow?: boolean;
generateDashboardPreviews?: boolean;
owner?: string;
pullRequestLinter?: boolean;
repository?: string;
token?: string;
webhookSecret?: string;
webhookURL?: string;
};
export type LocalRepositoryConfig = {
path?: string;
};
export type S3RepositoryConfig = {
bucket?: string;
region?: string;
};
export type RepositorySpec = {
description?: string;
editing: EditingOptions;
folder?: string;
github?: GitHubRepositoryConfig;
linting?: boolean;
local?: LocalRepositoryConfig;
preferYaml?: boolean;
s3?: S3RepositoryConfig;
title: string;
type: 'github' | 'local' | 's3';
};
export type HealthStatus = {
checked?: number;
healthy: boolean;
message?: string[];
};
export type SyncStatus = {
finished?: number;
hash?: string;
job?: string;
message?: string[];
scheduled?: number;
started?: number;
state: 'error' | 'pending' | 'success' | 'working';
};
export type RepositoryStatus = {
health: HealthStatus;
sync: SyncStatus;
};
export type Repository = {
apiVersion?: string;
kind?: string;
metadata?: ObjectMeta;
spec?: RepositorySpec;
status?: RepositoryStatus;
};
export type RepositoryList = {
apiVersion?: string;
items?: Repository[];
kind?: string;
metadata?: ListMeta;
};
export type StatusCause = {
field?: string;
message?: string;
reason?: string;
};
export type StatusDetails = {
causes?: StatusCause[];
group?: string;
kind?: string;
name?: string;
retryAfterSeconds?: number;
uid?: string;
};
export type Status = {
apiVersion?: string;
code?: number;
details?: StatusDetails;
kind?: string;
message?: string;
metadata?: ListMeta;
reason?: string;
status?: string;
};
export type Preconditions = {
resourceVersion?: string;
uid?: string;
};
export type DeleteOptions = {
apiVersion?: string;
dryRun?: string[];
gracePeriodSeconds?: number;
kind?: string;
orphanDependents?: boolean;
preconditions?: Preconditions;
propagationPolicy?: string;
};
export type Patch = object;
export type LintIssue = {
message: string;
rule: string;
severity: 'error' | 'exclude' | 'fixed' | 'quiet' | 'warning';
};
export type Unstructured = {
[key: string]: any;
};
export type ResourceType = {
classic?: 'access-control' | 'alerting' | 'dashboard' | 'datasources';
group?: string;
kind?: string;
resource?: string;
version?: string;
};
export type ResourceObjects = {
action?: 'create' | 'delete' | 'update';
dryRun?: Unstructured;
existing?: Unstructured;
file?: Unstructured;
type: ResourceType;
};
export type ResourceWrapper = {
apiVersion?: string;
errors?: string[];
hash?: string;
kind?: string;
lint?: LintIssue[];
path?: string;
ref?: string;
resource: ResourceObjects;
timestamp?: Time;
};
export type TestResults = {
apiVersion?: string;
code: number;
details?: Unstructured;
errors?: string[];
kind?: string;
success: boolean;
};
export type WebhookResponse = {
added?: string;
apiVersion?: string;
code?: number;
job?: JobSpec;
kind?: string;
};
export const {
useListJobQuery,
useGetJobQuery,
useListRepositoryQuery,
useListJobsQuery,
useCreateRepositoryMutation,
useGetRepositoryQuery,
useUpdateRepositoryMutation,
useReplaceRepositoryMutation,
useDeleteRepositoryMutation,
usePatchRepositoryMutation,
useUpdateRepositoryMutation,
useCreateRepositoryExportMutation,
useGetRepositoryFilesQuery,
useListRepositoryFilesQuery,
useListRepositoryFileHistoryQuery,
useUpdateRepositoryFilesMutation,
useCreateRepositoryFilesMutation,
useDeleteRepositoryFilesMutation,
useCreateRepositorySyncMutation,
useGetRepositoryFilesWithPathQuery,
usePutRepositoryFilesWithPathMutation,
useCreateRepositoryFilesWithPathMutation,
useDeleteRepositoryFilesWithPathMutation,
useGetRepositoryHistoryQuery,
useGetRepositoryHistoryWithPathQuery,
useGetRepositoryStatusQuery,
useReplaceRepositoryStatusMutation,
useUpdateRepositoryStatusMutation,
usePatchRepositoryStatusMutation,
useTestRepositoryQuery,
useTestRepositoryConfigMutation,
useCreateRepositorySyncMutation,
useCreateRepositoryTestMutation,
useGetRepositoryWebhookQuery,
useCreateRepositoryWebhookMutation,
} = injectedRtkApi;
function getListParams(queryArg: ListApiArg | void) {
if (!queryArg) {
return undefined;
}
const { fieldSelector, labelSelector, ...params } = queryArg;
return {
fieldSelector: fieldSelector ? parseListOptionsSelector(fieldSelector) : undefined,
labelSelector: labelSelector ? parseListOptionsSelector(labelSelector) : undefined,
...params,
};
}
+31 -1
View File
@@ -1,4 +1,34 @@
import { parseListOptionsSelector } from '../../apiserver/client';
import { ListOptions } from '../../apiserver/types';
export * from './endpoints';
import { generatedAPI } from './endpoints';
export const provisioningAPI = generatedAPI.enhanceEndpoints({});
export const provisioningAPI = generatedAPI.enhanceEndpoints({
endpoints: {
listJob(endpoint) {
endpoint.query = (queryArg) => ({
url: `/jobs`,
params: getListParams(queryArg),
});
},
listRepository(endpoint) {
endpoint.query = (queryArg) => ({
url: `/repositories`,
params: getListParams(queryArg),
});
},
},
});
function getListParams<T extends ListOptions>(queryArg: T | void) {
if (!queryArg) {
return undefined;
}
const { fieldSelector, labelSelector, ...params } = queryArg;
return {
fieldSelector: fieldSelector ? parseListOptionsSelector(fieldSelector) : undefined,
labelSelector: labelSelector ? parseListOptionsSelector(labelSelector) : undefined,
...params,
};
}
+11 -9
View File
@@ -3,13 +3,15 @@ import { useCallback } from 'react';
import { useUrlParams } from 'app/core/navigation/hooks';
import {
useCreateRepositoryFilesMutation,
Repository,
useCreateRepositoryFilesWithPathMutation,
useCreateRepositoryMutation,
useListRepositoryQuery,
useUpdateRepositoryFilesMutation,
usePutRepositoryFilesWithPathMutation,
useUpdateRepositoryMutation,
RepositorySpec,
PutRepositoryFilesWithPathArg,
} from './api';
import { FileOperationArg, RepositoryResource, RepositorySpec } from './api/types';
export function useCreateOrUpdateRepository(name?: string) {
const [create, createRequest] = useCreateRepositoryMutation();
@@ -20,7 +22,7 @@ export function useCreateOrUpdateRepository(name?: string) {
if (name) {
return update({ name, body: { metadata: { name }, spec: data } });
}
return create({ metadata: { generateName: 'repository' }, spec: data });
return create({ body: { metadata: { generateName: 'repository' }, spec: data } });
},
[create, name, update]
);
@@ -29,22 +31,22 @@ export function useCreateOrUpdateRepository(name?: string) {
}
// Sort repositories by resourceVersion to show the last modified
export function useRepositoryList(): [RepositoryResource[] | undefined, boolean] {
export function useRepositoryList(): [Repository[] | undefined, boolean] {
const query = useListRepositoryQuery();
const sortedItems = query.data?.items?.slice().sort((a, b) => {
return Number(b.metadata.resourceVersion) - Number(a.metadata.resourceVersion);
return Number(b.metadata?.resourceVersion) - Number(a.metadata?.resourceVersion);
});
return [sortedItems, query.isLoading];
}
export function useCreateOrUpdateRepositoryFile(name?: string) {
const [create, createRequest] = useCreateRepositoryFilesMutation();
const [update, updateRequest] = useUpdateRepositoryFilesMutation();
const [create, createRequest] = useCreateRepositoryFilesWithPathMutation();
const [update, updateRequest] = usePutRepositoryFilesWithPathMutation();
const updateOrCreate = useCallback(
(data: FileOperationArg) => {
(data: PutRepositoryFilesWithPathArg) => {
const actions = name ? update : create;
return actions(data);
},
@@ -1,11 +1,11 @@
import { RepositorySpec } from '../api/types';
import { RepositorySpec } from '../api';
import { RepositoryFormData } from '../types';
export const dataToSpec = (data: RepositoryFormData): RepositorySpec => {
const spec: RepositorySpec = {
type: data.type,
folder: data.folder,
title: data.title,
title: data.title || '',
editing: data.editing,
linting: data.linting,
preferYaml: data.preferYaml,
@@ -1,4 +1,4 @@
import { RepositorySpec } from '../api/types';
import { RepositorySpec } from '../api';
export function createPRLink(spec?: RepositorySpec, dashboardName?: string, ref?: string) {
if (!spec || spec.type !== 'github' || !ref) {
+20 -1
View File
@@ -38,10 +38,29 @@ const config: ConfigFile = {
},
'../public/app/features/provisioning/api/endpoints.gen.ts': {
apiFile: '../public/app/features/provisioning/api/baseAPI.ts',
schemaFile: '../public/app/features/provisioning/api/spec.json',
schemaFile: '../pkg/tests/apis/provisioning/testdata/openapi.json',
apiImport: 'baseAPI',
filterEndpoints,
argSuffix: 'Arg',
responseSuffix: 'Response',
tag: true,
endpointOverrides: [
{
pattern: /^list/,
parameterFilter: () => false,
},
],
},
},
};
function filterEndpoints(name: string) {
return (
!name.toLowerCase().includes('forallnamespaces') &&
!name.toLowerCase().includes('getapiresources') &&
!name.toLowerCase().includes('watch') &&
!name.toLowerCase().includes('collection')
);
}
export default config;
+65
View File
@@ -0,0 +1,65 @@
const fs = require('fs');
const path = require('path');
// Read the content of the file
const filePath = path.resolve(__dirname, '../public/app/features/provisioning/api/endpoints.gen.ts');
const outputFilePath = path.resolve(__dirname, '../public/app/features/provisioning/api/endpoints.ts');
const content = fs.readFileSync(filePath, 'utf-8');
// Define the prefixes to remove
const prefixes = [
'ComGithubGrafanaGrafanaPkgApisProvisioningV0Alpha1',
'ComGithubGrafanaGrafanaPkgApimachineryApisCommonV0Alpha1',
'IoK8SApimachineryPkgApisMetaV1',
'IoK8SApimachineryPkgRuntimeRaw',
];
// Function to create a regex for each prefix
const createPrefixRegex = (prefixes: string[]): RegExp => {
return new RegExp(prefixes.join('|'), 'g');
};
// Function to remove duplicate types from union type definitions
const removeDuplicateTypes = (typeDef: string): string => {
const typeSet = new Set(typeDef.split('|').map((type) => type.trim()));
return Array.from(typeSet).join(' | ');
};
// Perform transformations
const transformedContent = content
// Remove comments (block comments and line comments)
.replace(/(?:\/\*\*[\s\S]*?\*\/)|(?:\/\/.*)/g, '')
// Remove namespace property from all types
.replace(/namespace: string;/g, '')
// Remove any lines that are now just whitespace or empty
.replace(/^\s*[\r\n]/gm, '')
// Remove specified prefixes
.replace(createPrefixRegex(prefixes), '')
// Remove the specific URL segment
.replace(/\/apis\/provisioning\.grafana\.app\/v0alpha1\/namespaces\/\$\{queryArg\['namespace']}/g, '')
// Replace lowercase 'com' prefix specific match with 'body'
.replace(/\bcomGithubGrafanaGrafanaPkgApisProvisioningV0Alpha1[A-Za-z0-9_]*/g, 'body')
// Replace ioK8SApimachineryPkgApisMetaV1 prefix specific match with 'body'
.replace(/\bioK8SApimachineryPkgApisMetaV1[A-Za-z0-9_]*/g, 'body')
// Remove duplicate types in union type definitions
.replace(/(export type [A-Za-z0-9]+ = [^;]+);/g, (match: string, typeDef: string) => {
return `${removeDuplicateTypes(typeDef)};`;
})
// Modify any 'List...Arg>' to 'List...Arg | void>'
.replace(/(List[A-Za-z0-9]+Arg)>/g, '$1 | void>')
// Add headers for content-type in patch endpoints
.replace(/(method: 'PATCH',)/g, "$1\n\t\t\t\theaders: { 'Content-Type': 'application/merge-patch+json' },");
// Write the transformed content back to a new file
fs.writeFileSync(outputFilePath, transformedContent);
console.log('Transformation complete! Check the transformed file at:', outputFilePath);
+58 -18
View File
@@ -6909,20 +6909,21 @@ __metadata:
languageName: node
linkType: hard
"@rtk-query/codegen-openapi@npm:^1.2.0":
version: 1.2.0
resolution: "@rtk-query/codegen-openapi@npm:1.2.0"
"@rtk-query/codegen-openapi@npm:^2.0.0":
version: 2.0.0
resolution: "@rtk-query/codegen-openapi@npm:2.0.0"
dependencies:
"@apidevtools/swagger-parser": "npm:^10.0.2"
commander: "npm:^6.2.0"
oazapfts: "npm:^4.8.0"
prettier: "npm:^2.2.1"
lodash.camelcase: "npm:^4.3.0"
oazapfts: "npm:^6.1.0"
prettier: "npm:^3.2.5"
semver: "npm:^7.3.5"
swagger2openapi: "npm:^7.0.4"
typescript: "npm:^5.0.0"
typescript: "npm:^5.5.4"
bin:
rtk-query-codegen-openapi: lib/bin/cli.js
checksum: 10/74b5d68b40322e6f30fff3f0250bccf378e5408e24bb47c109ad31e3c45a53c99d67b0ea0685ab0abcfc2e598eec9bdfc94aee241b61049ac862b77bd9780518
rtk-query-codegen-openapi: lib/bin/cli.mjs
checksum: 10/c1dbab43e950c4defc1246034506c770a6a742aaf86a991c1cdbac0ac63cd2232c14bbe05d8c29421339b5e3aeb75740de3d709c5503f2af0b746e71b46caa87
languageName: node
linkType: hard
@@ -17607,7 +17608,7 @@ __metadata:
"@react-types/shared": "npm:3.26.0"
"@reduxjs/toolkit": "npm:2.5.0"
"@rsdoctor/webpack-plugin": "npm:^0.4.6"
"@rtk-query/codegen-openapi": "npm:^1.2.0"
"@rtk-query/codegen-openapi": "npm:^2.0.0"
"@rtsao/plugin-proposal-class-properties": "npm:7.0.1-patch.1"
"@stylistic/eslint-plugin-ts": "npm:^2.9.0"
"@swc/core": "npm:1.10.1"
@@ -21169,6 +21170,13 @@ __metadata:
languageName: node
linkType: hard
"lodash.camelcase@npm:^4.3.0":
version: 4.3.0
resolution: "lodash.camelcase@npm:4.3.0"
checksum: 10/c301cc379310441dc73cd6cebeb91fb254bea74e6ad3027f9346fc43b4174385153df420ffa521654e502fd34c40ef69ca4e7d40ee7129a99e06f306032bfc65
languageName: node
linkType: hard
"lodash.debounce@npm:^4, lodash.debounce@npm:^4.0.8":
version: 4.0.8
resolution: "lodash.debounce@npm:4.0.8"
@@ -22957,18 +22965,21 @@ __metadata:
languageName: node
linkType: hard
"oazapfts@npm:^4.8.0":
version: 4.12.0
resolution: "oazapfts@npm:4.12.0"
"oazapfts@npm:^6.1.0":
version: 6.1.0
resolution: "oazapfts@npm:6.1.0"
dependencies:
"@apidevtools/swagger-parser": "npm:^10.1.0"
lodash: "npm:^4.17.21"
minimist: "npm:^1.2.8"
swagger2openapi: "npm:^7.0.8"
typescript: "npm:^5.2.2"
tapable: "npm:^2.2.1"
typescript: "npm:^5.4.5"
peerDependencies:
"@oazapfts/runtime": "*"
bin:
oazapfts: lib/codegen/cli.js
checksum: 10/993d2cebfb2aab6178893e63039eda5968248684e02cf7e3df41e8fbc57d3a551c1c4a7b8bc3f1384f9a10557e9fd71717eeef8b1a38948a412ba46c5271f411
oazapfts: cli.js
checksum: 10/804c3936702acf66bb9bcff6bdcdce2708783b31c83262d7097fb04a53d3720d96114cec345408cbf3f933f9c5d198815457853a07362796c6a6d5db7a31d150
languageName: node
linkType: hard
@@ -24447,7 +24458,7 @@ __metadata:
languageName: node
linkType: hard
"prettier@npm:^2.2.1, prettier@npm:^2.3.2, prettier@npm:^2.8.7":
"prettier@npm:^2.3.2, prettier@npm:^2.8.7":
version: 2.8.8
resolution: "prettier@npm:2.8.8"
bin:
@@ -24456,6 +24467,15 @@ __metadata:
languageName: node
linkType: hard
"prettier@npm:^3.2.5":
version: 3.4.2
resolution: "prettier@npm:3.4.2"
bin:
prettier: bin/prettier.cjs
checksum: 10/a3e806fb0b635818964d472d35d27e21a4e17150c679047f5501e1f23bd4aa806adf660f0c0d35214a210d5d440da6896c2e86156da55f221a57938278dc326e
languageName: node
linkType: hard
"pretty-bytes@npm:^5.6.0":
version: 5.6.0
resolution: "pretty-bytes@npm:5.6.0"
@@ -29733,7 +29753,7 @@ __metadata:
languageName: node
linkType: hard
"typescript@npm:5.5.4, typescript@npm:>=2.7, typescript@npm:>=3 < 6, typescript@npm:^5.0.0, typescript@npm:^5.0.4, typescript@npm:^5.2.2":
"typescript@npm:5.5.4, typescript@npm:>=2.7, typescript@npm:>=3 < 6, typescript@npm:^5.0.4":
version: 5.5.4
resolution: "typescript@npm:5.5.4"
bin:
@@ -29743,7 +29763,17 @@ __metadata:
languageName: node
linkType: hard
"typescript@patch:typescript@npm%3A5.5.4#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A>=2.7#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A>=3 < 6#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.0.0#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.2.2#optional!builtin<compat/typescript>":
"typescript@npm:^5.4.5, typescript@npm:^5.5.4":
version: 5.7.2
resolution: "typescript@npm:5.7.2"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10/4caa3904df69db9d4a8bedc31bafc1e19ffb7b24fbde2997a1633ae1398d0de5bdbf8daf602ccf3b23faddf1aeeb9b795223a2ed9c9a4fdcaf07bfde114a401a
languageName: node
linkType: hard
"typescript@patch:typescript@npm%3A5.5.4#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A>=2.7#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A>=3 < 6#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>":
version: 5.5.4
resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin<compat/typescript>::version=5.5.4&hash=379a07"
bin:
@@ -29753,6 +29783,16 @@ __metadata:
languageName: node
linkType: hard
"typescript@patch:typescript@npm%3A^5.4.5#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.5.4#optional!builtin<compat/typescript>":
version: 5.7.2
resolution: "typescript@patch:typescript@npm%3A5.7.2#optional!builtin<compat/typescript>::version=5.7.2&hash=5786d5"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10/d75ca10141afc64fd3474b41a8b082b640555bed388d237558aed64e5827ddadb48f90932c7f4205883f18f5bcab8b6a739a2cfac95855604b0dfeb34bc2f3eb
languageName: node
linkType: hard
"ua-parser-js@npm:^1.0.32":
version: 1.0.33
resolution: "ua-parser-js@npm:1.0.33"