API clients: Extract into a package (#111810)
Co-authored-by: Tom Ratcliffe <tom.ratcliffe@grafana.com> Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
This commit is contained in:
co-authored by
Alex Khomenko
parent
91b7ff2ece
commit
811ee99dac
+3
-4
@@ -741,6 +741,9 @@
|
||||
# @grafana/test-utils
|
||||
/packages/grafana-test-utils @grafana/grafana-frontend-platform
|
||||
|
||||
# @grafana/api-clients
|
||||
/packages/grafana-api-clients/ @grafana/grafana-frontend-platform @grafana/grafana-search-navigate-organise
|
||||
|
||||
# root files, mostly frontend
|
||||
/.browserslistrc @grafana/frontend-ops
|
||||
/package.json @grafana/frontend-ops
|
||||
@@ -959,7 +962,6 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
|
||||
/public/app/features/transformers/timeSeriesTable/ @grafana/dataviz-squad @grafana/app-o11y-visualizations
|
||||
/public/app/features/users/ @grafana/access-squad
|
||||
/public/app/features/variables/ @grafana/dashboards-squad
|
||||
/public/app/features/preferences/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/bookmarks/ @grafana/grafana-search-navigate-organise
|
||||
/public/app/plugins/panel/* @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/alertlist/ @grafana/alerting-frontend
|
||||
@@ -1055,13 +1057,10 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
|
||||
/scripts/trigger_windows_build.sh @grafana/grafana-developer-enablement-squad
|
||||
/scripts/cleanup-husky.sh @grafana/frontend-ops
|
||||
/scripts/verify-repo-update/ @grafana/grafana-developer-enablement-squad
|
||||
/scripts/generate-rtk-apis.ts @grafana/grafana-frontend-platform
|
||||
/scripts/process-specs.ts @grafana/grafana-frontend-platform
|
||||
/scripts/generate-alerting-rtk-apis.ts @grafana/alerting-frontend
|
||||
/scripts/levitate-parse-json-report.js @grafana/plugins-platform-frontend
|
||||
/scripts/levitate-show-affected-plugins.js @grafana/plugins-platform-frontend
|
||||
/scripts/codemods/explicit-barrel-imports.cjs @grafana/frontend-ops
|
||||
/scripts/rtk-client-generator/ @grafana/grafana-search-navigate-organise
|
||||
|
||||
/scripts/codeowners-manifest/ @grafana/dataviz-squad
|
||||
/scripts/test-coverage-by-codeowner.js @grafana/dataviz-squad
|
||||
|
||||
@@ -3248,11 +3248,6 @@
|
||||
"count": 4
|
||||
}
|
||||
},
|
||||
"public/app/features/migrate-to-cloud/api/index.ts": {
|
||||
"no-barrel-files/no-barrel-files": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"public/app/features/migrate-to-cloud/cloud/MigrationTokenPane/CreateTokenModal.tsx": {
|
||||
"no-restricted-syntax": {
|
||||
"count": 1
|
||||
|
||||
+3
-5
@@ -75,9 +75,8 @@
|
||||
"plugin:build:dev": "nx run-many -t dev --projects='tag:scope:plugin' --maxParallel=100",
|
||||
"plugin:test:ci": "nx run-many -t test:ci --projects='tag:scope:plugin' --maxParallel=2",
|
||||
"plugin:i18n-extract": "nx run-many -t i18n-extract --projects='tag:scope:plugin'",
|
||||
"process-specs": "node --experimental-strip-types scripts/process-specs.ts",
|
||||
"generate-apis": "yarn process-specs && rtk-query-codegen-openapi ./scripts/generate-rtk-apis.ts",
|
||||
"generate:api-client": "NODE_OPTIONS='--experimental-strip-types --disable-warning=ExperimentalWarning' plop --plopfile scripts/rtk-client-generator/plopfile.ts"
|
||||
"generate-apis": "yarn workspace @grafana/api-clients generate-apis",
|
||||
"generate:api-client": "yarn workspace @grafana/api-clients generate:api-client"
|
||||
},
|
||||
"grafana": {
|
||||
"whatsNewUrl": "https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-v%[1]s-%[2]s/",
|
||||
@@ -231,11 +230,9 @@
|
||||
"node-notifier": "10.0.1",
|
||||
"nx": "21.3.11",
|
||||
"open": "^10.2.0",
|
||||
"openapi-types": "^12.1.3",
|
||||
"ora": "^9.0.0",
|
||||
"pa11y-ci": "^4.0.0",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"plop": "^4.0.1",
|
||||
"postcss": "8.5.6",
|
||||
"postcss-loader": "8.1.1",
|
||||
"postcss-reporter": "7.1.0",
|
||||
@@ -280,6 +277,7 @@
|
||||
"@formatjs/intl-durationformat": "^0.7.0",
|
||||
"@glideapps/glide-data-grid": "^6.0.0",
|
||||
"@grafana/alerting": "workspace:*",
|
||||
"@grafana/api-clients": "workspace:*",
|
||||
"@grafana/assistant": "0.1.0",
|
||||
"@grafana/aws-sdk": "0.7.1",
|
||||
"@grafana/azure-sdk": "0.0.7",
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ All packages are versioned according to the current Grafana version:
|
||||
- Grafana v6.2.5 -> @grafana/\* packages @ 6.2.5
|
||||
- Grafana - main branch version (based on package.json, i.e. 6.4.0-pre) -> @grafana/\* packages @ 6.4.0-pre-<COMMIT-SHA> (see details below about packages publishing channels)
|
||||
|
||||
> Please note that @grafana/ui, @grafana/data, and @grafana/runtime packages are considered ALPHA even though they are not released as alpha versions.
|
||||
> Please note that the @grafana/api-clients package is considered ALPHA even though it is not released as an alpha version.
|
||||
|
||||
### Stable releases
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Initial scaffold
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2015 Grafana Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Grafana API Clients
|
||||
|
||||
> **@grafana/api-clients is currently in BETA**.
|
||||
|
||||
This package hosts reusable clients for Grafana APIs. The implementation is currently in BETA and not yet published.
|
||||
|
||||
## Generating RTK Query API Clients
|
||||
|
||||
This guide explains how to generate API clients for Grafana APIs using this package.
|
||||
|
||||
### 1. Generate an OpenAPI snapshot
|
||||
|
||||
First, check if the `group` and the `version` are already present in [openapi_test.go](../../pkg/tests/apis/openapi_test.go). If so, move on to the next step. If you need to add a new block, you can check for the right `group` and `version` in the backend API call that you want to replicate in the frontend.
|
||||
|
||||
```go
|
||||
{
|
||||
Group: "dashboard.grafana.app",
|
||||
Version: "v0alpha1",
|
||||
}
|
||||
```
|
||||
|
||||
Afterwards, you need to run the `TestIntegrationOpenAPIs` test. Note that it will fail the first time you run it. On the second run, it will generate the corresponding OpenAPI spec, which you can find in [openapi_snapshots](../../pkg/tests/apis/openapi_snapshots).
|
||||
|
||||
> Note: You don't need to follow these two steps if the `group` you're working with is already in the `openapi_test.go` file.
|
||||
|
||||
### 2. Run the API generator script
|
||||
|
||||
Run `yarn generate:api-client` and follow the prompts. See [API Client Generator](./src/generator/README.md) for details.
|
||||
|
||||
## Updating generated clients
|
||||
|
||||
To update the existing clients, for example, when the OpenAPI spec has changed, run `yarn generate-apis`. This will regenerate all the clients based on the current OpenAPI snapshots.
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"name": "@grafana/api-clients",
|
||||
"version": "12.3.0-pre",
|
||||
"description": "Grafana API client utilities",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
"typescript"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/grafana/grafana.git",
|
||||
"directory": "packages/grafana-api-clients"
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
"module": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": "./src/index.ts",
|
||||
"require": "./src/index.ts"
|
||||
},
|
||||
"./rtkq": {
|
||||
"import": "./src/clients/rtkq/index.ts",
|
||||
"require": "./src/clients/rtkq/index.ts"
|
||||
},
|
||||
"./rtkq/advisor/v0alpha1": {
|
||||
"import": "./src/clients/rtkq/advisor/v0alpha1/index.ts",
|
||||
"require": "./src/clients/rtkq/advisor/v0alpha1/index.ts"
|
||||
},
|
||||
"./rtkq/dashboard/v0alpha1": {
|
||||
"import": "./src/clients/rtkq/dashboard/v0alpha1/index.ts",
|
||||
"require": "./src/clients/rtkq/dashboard/v0alpha1/index.ts"
|
||||
},
|
||||
"./rtkq/folder/v1beta1": {
|
||||
"import": "./src/clients/rtkq/folder/v1beta1/index.ts",
|
||||
"require": "./src/clients/rtkq/folder/v1beta1/index.ts"
|
||||
},
|
||||
"./rtkq/iam/v0alpha1": {
|
||||
"import": "./src/clients/rtkq/iam/v0alpha1/index.ts",
|
||||
"require": "./src/clients/rtkq/iam/v0alpha1/index.ts"
|
||||
},
|
||||
"./rtkq/legacy/migrate-to-cloud": {
|
||||
"import": "./src/clients/rtkq/migrate-to-cloud/index.ts",
|
||||
"require": "./src/clients/rtkq/migrate-to-cloud/index.ts"
|
||||
},
|
||||
"./rtkq/legacy/preferences": {
|
||||
"import": "./src/clients/rtkq/preferences/user/index.ts",
|
||||
"require": "./src/clients/rtkq/preferences/user/index.ts"
|
||||
},
|
||||
"./rtkq/legacy/user": {
|
||||
"import": "./src/clients/rtkq/user/index.ts",
|
||||
"require": "./src/clients/rtkq/user/index.ts"
|
||||
},
|
||||
"./rtkq/playlist/v0alpha1": {
|
||||
"import": "./src/clients/rtkq/playlist/v0alpha1/index.ts",
|
||||
"require": "./src/clients/rtkq/playlist/v0alpha1/index.ts"
|
||||
},
|
||||
"./rtkq/preferences/v1alpha1": {
|
||||
"import": "./src/clients/rtkq/preferences/v1alpha1/index.ts",
|
||||
"require": "./src/clients/rtkq/preferences/v1alpha1/index.ts"
|
||||
},
|
||||
"./rtkq/provisioning/v0alpha1": {
|
||||
"import": "./src/clients/rtkq/provisioning/v0alpha1/index.ts",
|
||||
"require": "./src/clients/rtkq/provisioning/v0alpha1/index.ts"
|
||||
},
|
||||
"./rtkq/shorturl/v1alpha1": {
|
||||
"import": "./src/clients/rtkq/shorturl/v1alpha1/index.ts",
|
||||
"require": "./src/clients/rtkq/shorturl/v1alpha1/index.ts"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"main": "./dist/cjs/index.cjs",
|
||||
"module": "./dist/esm/index.mjs",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"./dist",
|
||||
"./README.md",
|
||||
"./CHANGELOG.md",
|
||||
"./LICENSE_APACHE2"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc -p ./tsconfig.build.json && rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"bundle": "rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"clean": "rimraf ./dist ./compiled ./package.tgz",
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
|
||||
"prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js",
|
||||
"postpack": "mv package.json.bak package.json",
|
||||
"process-specs": "NODE_OPTIONS='--disable-warning=ExperimentalWarning' node --experimental-strip-types src/scripts/process-specs.ts",
|
||||
"generate-apis": "yarn process-specs && NODE_OPTIONS='--disable-warning=ExperimentalWarning' rtk-query-codegen-openapi ./src/scripts/generate-rtk-apis.ts",
|
||||
"generate:api-client": "NODE_OPTIONS='--experimental-strip-types --disable-warning=ExperimentalWarning' plop --plopfile src/generator/plopfile.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rtk-query/codegen-openapi": "^2.0.0",
|
||||
"openapi-types": "^12.1.3",
|
||||
"plop": "^4.0.1",
|
||||
"rollup": "^4.22.4",
|
||||
"typescript": "5.9.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@grafana/runtime": ">=11.6 <= 12.x",
|
||||
"@reduxjs/toolkit": "^2.8.0",
|
||||
"rxjs": "7.8.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "library",
|
||||
"tags": ["scope:package", "type:runtime"],
|
||||
"targets": {
|
||||
"build": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
import { cjsOutput, entryPoint, esmOutput, plugins } from '../rollup.config.parts';
|
||||
|
||||
const rq = createRequire(import.meta.url);
|
||||
const pkg = rq('./package.json');
|
||||
|
||||
const apiClients = Object.entries<{ import: string; require: string }>(pkg.exports).filter(([key]) =>
|
||||
key.startsWith('./rtkq/')
|
||||
);
|
||||
|
||||
const apiClientConfigs = apiClients.map(([name, { import: importPath }]) => {
|
||||
const baseCjsOutput = cjsOutput(pkg);
|
||||
const entryFileNames = name.replace('./', '') + '.cjs';
|
||||
const cjsOutputConfig = { ...baseCjsOutput, entryFileNames };
|
||||
return {
|
||||
input: importPath.replace('./', ''),
|
||||
|
||||
plugins,
|
||||
output: [cjsOutputConfig, esmOutput(pkg, 'grafana-api-clients')],
|
||||
treeshake: false,
|
||||
};
|
||||
});
|
||||
|
||||
export default [
|
||||
{
|
||||
input: entryPoint,
|
||||
plugins,
|
||||
output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-api-clients')],
|
||||
treeshake: false,
|
||||
},
|
||||
{
|
||||
input: 'src/clients/rtkq/index.ts',
|
||||
plugins,
|
||||
output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-api-clients')],
|
||||
treeshake: false,
|
||||
},
|
||||
...apiClientConfigs,
|
||||
];
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { getAPIBaseURL } from '../../../../utils/utils';
|
||||
import { createBaseQuery } from '../../createBaseQuery';
|
||||
|
||||
export const API_GROUP = 'advisor.grafana.app' as const;
|
||||
export const API_VERSION = 'v0alpha1' as const;
|
||||
export const BASE_URL = getAPIBaseURL(API_GROUP, API_VERSION);
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'advisorAPIv0alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
export { BASE_URL, API_GROUP, API_VERSION } from './baseAPI';
|
||||
import { generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export * from './endpoints.gen';
|
||||
export const generatedAPI = rawAPI.enhanceEndpoints({});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { getAPIBaseURL } from '../../../../utils/utils';
|
||||
import { createBaseQuery } from '../../createBaseQuery';
|
||||
|
||||
export const API_GROUP = 'correlations.grafana.app' as const;
|
||||
export const API_VERSION = 'v0alpha1' as const;
|
||||
export const BASE_URL = getAPIBaseURL(API_GROUP, API_VERSION);
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'correlationsAPIv0alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
+4
-2
@@ -1,6 +1,8 @@
|
||||
import { generatedAPI } from './endpoints.gen';
|
||||
export { BASE_URL, API_GROUP, API_VERSION } from './baseAPI';
|
||||
import { generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export const correlationAPIv0alpha1 = generatedAPI.enhanceEndpoints({
|
||||
export * from './endpoints.gen';
|
||||
export const generatedAPI = rawAPI.enhanceEndpoints({
|
||||
endpoints: {
|
||||
createCorrelation: (endpointDefinition) => {
|
||||
const originalQuery = endpointDefinition.query;
|
||||
+4
-10
@@ -1,9 +1,11 @@
|
||||
import { BaseQueryFn } from '@reduxjs/toolkit/query';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
import { BackendSrvRequest, getBackendSrv, isFetchError } from '@grafana/runtime';
|
||||
import { type BackendSrvRequest, getBackendSrv } from '@grafana/runtime';
|
||||
|
||||
interface RequestOptions extends BackendSrvRequest {
|
||||
import { handleRequestError } from '../../utils/utils';
|
||||
|
||||
export interface RequestOptions extends BackendSrvRequest {
|
||||
manageError?: (err: unknown) => { error: unknown };
|
||||
body?: BackendSrvRequest['data'];
|
||||
}
|
||||
@@ -49,11 +51,3 @@ export function createBaseQuery({ baseURL }: CreateBaseQueryOptions): BaseQueryF
|
||||
|
||||
return backendSrvBaseQuery;
|
||||
}
|
||||
|
||||
export function handleRequestError(error: unknown) {
|
||||
if (isFetchError(error) || error instanceof Error) {
|
||||
return { error };
|
||||
} else {
|
||||
return { error: new Error('Unknown error') };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { getAPIBaseURL } from '../../../../utils/utils';
|
||||
import { createBaseQuery } from '../../createBaseQuery';
|
||||
|
||||
export const API_GROUP = 'dashboard.grafana.app' as const;
|
||||
export const API_VERSION = 'v0alpha1' as const;
|
||||
export const BASE_URL = getAPIBaseURL(API_GROUP, API_VERSION);
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'dashboardAPIv0alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
export { BASE_URL, API_GROUP, API_VERSION } from './baseAPI';
|
||||
import { addTagTypes, generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export * from './endpoints.gen';
|
||||
export const generatedAPI = rawAPI.enhanceEndpoints({
|
||||
addTagTypes: [...addTagTypes, 'Folder', 'Dashboard'],
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { getAPIBaseURL } from '../../../../utils/utils';
|
||||
import { createBaseQuery } from '../../createBaseQuery';
|
||||
|
||||
export const API_GROUP = 'folder.grafana.app' as const;
|
||||
export const API_VERSION = 'v1beta1' as const;
|
||||
export const BASE_URL = getAPIBaseURL(API_GROUP, API_VERSION);
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'folderAPIv1beta1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
export { BASE_URL, API_GROUP, API_VERSION } from './baseAPI';
|
||||
import { generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export * from './endpoints.gen';
|
||||
export const generatedAPI = rawAPI.enhanceEndpoints({});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { getAPIBaseURL } from '../../../../utils/utils';
|
||||
import { createBaseQuery } from '../../createBaseQuery';
|
||||
|
||||
export const API_GROUP = 'iam.grafana.app' as const;
|
||||
export const API_VERSION = 'v0alpha1' as const;
|
||||
export const BASE_URL = getAPIBaseURL(API_GROUP, API_VERSION);
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'iamAPIv0alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
export { BASE_URL, API_GROUP, API_VERSION } from './baseAPI';
|
||||
import { generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export * from './endpoints.gen';
|
||||
export const generatedAPI = rawAPI.enhanceEndpoints({});
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Don't manually add to this file! Use the `generate:api-client` command to add new API clients.
|
||||
*/
|
||||
import { generatedAPI as advisorAPIv0alpha1 } from './advisor/v0alpha1';
|
||||
import { generatedAPI as correlationsAPIv0alpha1 } from './correlations/v0alpha1';
|
||||
import { generatedAPI as dashboardAPIv0alpha1 } from './dashboard/v0alpha1';
|
||||
import { generatedAPI as folderAPIv1beta1 } from './folder/v1beta1';
|
||||
import { generatedAPI as iamAPIv0alpha1 } from './iam/v0alpha1';
|
||||
import { generatedAPI as migrateToCloudAPI } from './migrate-to-cloud';
|
||||
import { generatedAPI as playlistAPIv0alpha1 } from './playlist/v0alpha1';
|
||||
import { generatedAPI as preferencesUserAPI } from './preferences/user';
|
||||
import { generatedAPI as preferencesAPIv1alpha1 } from './preferences/v1alpha1';
|
||||
import { generatedAPI as provisioningAPIv0alpha1 } from './provisioning/v0alpha1';
|
||||
import { generatedAPI as shortURLAPIv1alpha1 } from './shorturl/v1alpha1';
|
||||
import { generatedAPI as legacyUserAPI } from './user';
|
||||
// PLOP_INJECT_IMPORT
|
||||
|
||||
/** RTK Query middleware for all API clients */
|
||||
export const allMiddleware = [
|
||||
advisorAPIv0alpha1.middleware,
|
||||
dashboardAPIv0alpha1.middleware,
|
||||
folderAPIv1beta1.middleware,
|
||||
iamAPIv0alpha1.middleware,
|
||||
migrateToCloudAPI.middleware,
|
||||
playlistAPIv0alpha1.middleware,
|
||||
preferencesAPIv1alpha1.middleware,
|
||||
preferencesUserAPI.middleware,
|
||||
provisioningAPIv0alpha1.middleware,
|
||||
shortURLAPIv1alpha1.middleware,
|
||||
correlationsAPIv0alpha1.middleware,
|
||||
legacyUserAPI.middleware,
|
||||
// PLOP_INJECT_MIDDLEWARE
|
||||
] as const;
|
||||
|
||||
/** RTK Query reducers for all API clients */
|
||||
export const allReducers = {
|
||||
[advisorAPIv0alpha1.reducerPath]: advisorAPIv0alpha1.reducer,
|
||||
[dashboardAPIv0alpha1.reducerPath]: dashboardAPIv0alpha1.reducer,
|
||||
[folderAPIv1beta1.reducerPath]: folderAPIv1beta1.reducer,
|
||||
[iamAPIv0alpha1.reducerPath]: iamAPIv0alpha1.reducer,
|
||||
[migrateToCloudAPI.reducerPath]: migrateToCloudAPI.reducer,
|
||||
[playlistAPIv0alpha1.reducerPath]: playlistAPIv0alpha1.reducer,
|
||||
[preferencesAPIv1alpha1.reducerPath]: preferencesAPIv1alpha1.reducer,
|
||||
[preferencesUserAPI.reducerPath]: preferencesUserAPI.reducer,
|
||||
[provisioningAPIv0alpha1.reducerPath]: provisioningAPIv0alpha1.reducer,
|
||||
[shortURLAPIv1alpha1.reducerPath]: shortURLAPIv1alpha1.reducer,
|
||||
[correlationsAPIv0alpha1.reducerPath]: correlationsAPIv0alpha1.reducer,
|
||||
[legacyUserAPI.reducerPath]: legacyUserAPI.reducer,
|
||||
// PLOP_INJECT_REDUCER
|
||||
};
|
||||
|
||||
export * from './createBaseQuery';
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
import { createBaseQuery } from '../createBaseQuery';
|
||||
|
||||
export const baseAPI = createApi({
|
||||
export const api = createApi({
|
||||
reducerPath: 'migrateToCloudGeneratedAPI',
|
||||
baseQuery: createBaseQuery({ baseURL: '/api' }),
|
||||
endpoints: () => ({}),
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { baseAPI as api } from './baseAPI';
|
||||
import { api } from './baseAPI';
|
||||
const injectedRtkApi = api.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
getSessionList: build.query<GetSessionListApiResponse, GetSessionListApiArg>({
|
||||
@@ -0,0 +1,89 @@
|
||||
import { BaseQueryFn, EndpointDefinition } from '@reduxjs/toolkit/query';
|
||||
|
||||
import { generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export const generatedAPI = rawAPI.enhanceEndpoints({
|
||||
addTagTypes: [
|
||||
'cloud-migration-token',
|
||||
'cloud-migration-session',
|
||||
'cloud-migration-snapshot',
|
||||
'cloud-migration-resource-dependencies',
|
||||
],
|
||||
|
||||
endpoints: {
|
||||
// Cloud-side - create token
|
||||
getCloudMigrationToken: {
|
||||
providesTags: ['cloud-migration-token'],
|
||||
},
|
||||
createCloudMigrationToken: {
|
||||
invalidatesTags: ['cloud-migration-token'],
|
||||
},
|
||||
deleteCloudMigrationToken: {
|
||||
invalidatesTags: ['cloud-migration-token'],
|
||||
},
|
||||
|
||||
// On-prem session management (entering token)
|
||||
getSessionList: {
|
||||
providesTags: ['cloud-migration-session'] /* should this be a -list? */,
|
||||
},
|
||||
getSession: {
|
||||
providesTags: ['cloud-migration-session'],
|
||||
},
|
||||
createSession: {
|
||||
invalidatesTags: ['cloud-migration-session'],
|
||||
},
|
||||
deleteSession: {
|
||||
invalidatesTags: ['cloud-migration-session', 'cloud-migration-snapshot'],
|
||||
},
|
||||
|
||||
// Snapshot management
|
||||
getShapshotList: {
|
||||
providesTags: ['cloud-migration-snapshot'],
|
||||
},
|
||||
getSnapshot: {
|
||||
providesTags: ['cloud-migration-snapshot'],
|
||||
},
|
||||
createSnapshot: {
|
||||
invalidatesTags: ['cloud-migration-snapshot'],
|
||||
},
|
||||
uploadSnapshot: {
|
||||
invalidatesTags: ['cloud-migration-snapshot'],
|
||||
},
|
||||
|
||||
// Resource dependencies
|
||||
getResourceDependencies: {
|
||||
providesTags: ['cloud-migration-resource-dependencies'],
|
||||
},
|
||||
|
||||
getDashboardByUid: suppressErrorsOnQuery,
|
||||
getLibraryElementByUid: suppressErrorsOnQuery,
|
||||
},
|
||||
});
|
||||
|
||||
function suppressErrorsOnQuery<
|
||||
QueryArg,
|
||||
BaseQuery extends BaseQueryFn,
|
||||
TagTypes extends string,
|
||||
ResultType,
|
||||
ReducerPath extends string,
|
||||
PageParam,
|
||||
>(endpoint: EndpointDefinition<QueryArg, BaseQuery, TagTypes, ResultType, ReducerPath, PageParam>) {
|
||||
if (!endpoint.query) {
|
||||
return;
|
||||
}
|
||||
|
||||
// internal type from rtk-query that isn't exported
|
||||
type InfiniteQueryCombinedArg<QueryArg, PageParam> = {
|
||||
queryArg: QueryArg;
|
||||
pageParam: PageParam;
|
||||
};
|
||||
|
||||
const originalQuery = endpoint.query;
|
||||
endpoint.query = (arg: QueryArg & InfiniteQueryCombinedArg<QueryArg, PageParam>) => {
|
||||
const baseQuery = originalQuery(arg);
|
||||
baseQuery.showErrorAlert = false;
|
||||
return baseQuery;
|
||||
};
|
||||
}
|
||||
|
||||
export * from './endpoints.gen';
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { getAPIBaseURL } from '../../../../utils/utils';
|
||||
import { createBaseQuery } from '../../createBaseQuery';
|
||||
|
||||
export const API_GROUP = 'playlist.grafana.app' as const;
|
||||
export const API_VERSION = 'v0alpha1' as const;
|
||||
export const BASE_URL = getAPIBaseURL(API_GROUP, API_VERSION);
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'playlistAPIv0alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
export { BASE_URL, API_GROUP, API_VERSION } from './baseAPI';
|
||||
import { generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export * from './endpoints.gen';
|
||||
export const generatedAPI = rawAPI.enhanceEndpoints({});
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
import { createBaseQuery } from '../../createBaseQuery';
|
||||
|
||||
export const baseAPI = createApi({
|
||||
export const api = createApi({
|
||||
reducerPath: 'userPreferencesAPI',
|
||||
baseQuery: createBaseQuery({ baseURL: '/api' }),
|
||||
endpoints: () => ({}),
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { baseAPI as api } from './baseAPI';
|
||||
import { api } from './baseAPI';
|
||||
const injectedRtkApi = api.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
getUserPreferences: build.query<GetUserPreferencesApiResponse, GetUserPreferencesApiArg>({
|
||||
+4
-5
@@ -1,9 +1,6 @@
|
||||
import { generatedAPI } from './user/endpoints.gen';
|
||||
import { generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export const { useGetUserPreferencesQuery, usePatchUserPreferencesMutation, useUpdateUserPreferencesMutation } =
|
||||
generatedAPI;
|
||||
|
||||
export const userPreferencesAPI = generatedAPI.enhanceEndpoints({
|
||||
export const generatedAPI = rawAPI.enhanceEndpoints({
|
||||
addTagTypes: ['UserPreferences'],
|
||||
endpoints: {
|
||||
getUserPreferences: {
|
||||
@@ -17,3 +14,5 @@ export const userPreferencesAPI = generatedAPI.enhanceEndpoints({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export * from './endpoints.gen';
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { getAPIBaseURL } from '../../../../utils/utils';
|
||||
import { createBaseQuery } from '../../createBaseQuery';
|
||||
|
||||
export const API_GROUP = 'preferences.grafana.app' as const;
|
||||
export const API_VERSION = 'v1alpha1' as const;
|
||||
export const BASE_URL = getAPIBaseURL(API_GROUP, API_VERSION);
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'preferencesAPIv1alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
export { BASE_URL, API_GROUP, API_VERSION } from './baseAPI';
|
||||
import { generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export * from './endpoints.gen';
|
||||
export const generatedAPI = rawAPI.enhanceEndpoints({});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { getAPIBaseURL } from '../../../../utils/utils';
|
||||
import { createBaseQuery } from '../../createBaseQuery';
|
||||
|
||||
export const API_GROUP = 'provisioning.grafana.app' as const;
|
||||
export const API_VERSION = 'v0alpha1' as const;
|
||||
export const BASE_URL = getAPIBaseURL(API_GROUP, API_VERSION);
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'provisioningAPIv0alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
export { BASE_URL, API_GROUP, API_VERSION } from './baseAPI';
|
||||
import { generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export * from './endpoints.gen';
|
||||
export const generatedAPI = rawAPI.enhanceEndpoints({});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { getAPIBaseURL } from '../../../../utils/utils';
|
||||
import { createBaseQuery } from '../../createBaseQuery';
|
||||
|
||||
export const API_GROUP = 'shorturl.grafana.app' as const;
|
||||
export const API_VERSION = 'v1alpha1' as const;
|
||||
export const BASE_URL = getAPIBaseURL(API_GROUP, API_VERSION);
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'shortURLAPIv1alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
export { BASE_URL, API_GROUP, API_VERSION } from './baseAPI';
|
||||
import { generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export * from './endpoints.gen';
|
||||
export const generatedAPI = rawAPI.enhanceEndpoints({});
|
||||
@@ -0,0 +1,9 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from '../createBaseQuery';
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'legacyUserAPI',
|
||||
baseQuery: createBaseQuery({ baseURL: '/api' }),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import { api } from './baseAPI';
|
||||
const injectedRtkApi = api.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
unstarDashboardByUid: build.mutation<UnstarDashboardByUidApiResponse, UnstarDashboardByUidApiArg>({
|
||||
query: (queryArg) => ({ url: `/user/stars/dashboard/uid/${queryArg.dashboardUid}`, method: 'DELETE' }),
|
||||
}),
|
||||
starDashboardByUid: build.mutation<StarDashboardByUidApiResponse, StarDashboardByUidApiArg>({
|
||||
query: (queryArg) => ({ url: `/user/stars/dashboard/uid/${queryArg.dashboardUid}`, method: 'POST' }),
|
||||
}),
|
||||
}),
|
||||
overrideExisting: false,
|
||||
});
|
||||
export { injectedRtkApi as generatedAPI };
|
||||
export type UnstarDashboardByUidApiResponse =
|
||||
/** status 200 An OKResponse is returned if the request was successful. */ SuccessResponseBody;
|
||||
export type UnstarDashboardByUidApiArg = {
|
||||
dashboardUid: string;
|
||||
};
|
||||
export type StarDashboardByUidApiResponse =
|
||||
/** status 200 An OKResponse is returned if the request was successful. */ SuccessResponseBody;
|
||||
export type StarDashboardByUidApiArg = {
|
||||
dashboardUid: string;
|
||||
};
|
||||
export type SuccessResponseBody = {
|
||||
message?: string;
|
||||
};
|
||||
export type ErrorResponseBody = {
|
||||
/** Error An optional detailed description of the actual error. Only included if running in developer mode. */
|
||||
error?: string;
|
||||
/** a human readable version of the error */
|
||||
message: string;
|
||||
/** Status An optional status to denote the cause of the error.
|
||||
|
||||
For example, a 412 Precondition Failed error may include additional information of why that error happened. */
|
||||
status?: string;
|
||||
};
|
||||
export const { useUnstarDashboardByUidMutation, useStarDashboardByUidMutation } = injectedRtkApi;
|
||||
@@ -0,0 +1,25 @@
|
||||
import { generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export const generatedAPI = rawAPI
|
||||
.enhanceEndpoints({
|
||||
addTagTypes: ['dashboardStars'],
|
||||
endpoints: {
|
||||
starDashboardByUid: {
|
||||
invalidatesTags: ['dashboardStars'],
|
||||
},
|
||||
unstarDashboardByUid: {
|
||||
invalidatesTags: ['dashboardStars'],
|
||||
},
|
||||
},
|
||||
})
|
||||
.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
getStars: build.query<string[], void>({
|
||||
query: () => ({ url: '/user/stars' }),
|
||||
providesTags: ['dashboardStars'],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useGetStarsQuery } = generatedAPI;
|
||||
export * from './endpoints.gen';
|
||||
+3
-1
@@ -26,7 +26,9 @@ The generator automates the following:
|
||||
- `scripts/generate-rtk-apis.ts` for OSS APIs
|
||||
- `local/generate-enterprise-apis.ts` for Enterprise APIs
|
||||
3. Creates the `index.ts` file with proper exports
|
||||
4. For OSS APIs only: Registers Redux reducers and middleware in the store. For Enterprise this needs to be done manually
|
||||
4. For OSS APIs only:
|
||||
- Exports Redux reducers and middleware in `rtkq.ts`
|
||||
- Updates `package.json` exports to include the new API client
|
||||
5. Formats all generated files using Prettier and ESLint
|
||||
6. Automatically runs the appropriate command to generate endpoints from the OpenAPI schema
|
||||
|
||||
+77
-5
@@ -1,4 +1,6 @@
|
||||
import { execSync } from 'child_process';
|
||||
import fs from 'fs';
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
import path from 'path';
|
||||
|
||||
type PlopActionFunction = (
|
||||
@@ -34,14 +36,24 @@ export const formatEndpoints = () => (endpointsInput: string | string[]) => {
|
||||
|
||||
// List of created or modified files
|
||||
export const getFilesToFormat = (groupName: string, version: string, isEnterprise = false) => {
|
||||
const apiClientBasePath = isEnterprise ? 'public/app/extensions/api/clients' : 'public/app/api/clients';
|
||||
const generateScriptPath = isEnterprise ? 'local/generate-enterprise-apis.ts' : 'scripts/generate-rtk-apis.ts';
|
||||
const apiClientBasePath = isEnterprise
|
||||
? 'public/app/extensions/api/clients'
|
||||
: 'packages/grafana-api-clients/src/clients/rtkq';
|
||||
const generateScriptPath = isEnterprise
|
||||
? 'local/generate-enterprise-apis.ts'
|
||||
: 'packages/grafana-api-clients/src/scripts/generate-rtk-apis.ts';
|
||||
|
||||
return [
|
||||
`${apiClientBasePath}/${groupName}/${version}/baseAPI.ts`,
|
||||
`${apiClientBasePath}/${groupName}/${version}/index.ts`,
|
||||
generateScriptPath,
|
||||
...(isEnterprise ? [] : [`public/app/core/reducers/root.ts`, `public/app/store/configureStore.ts`]),
|
||||
...(isEnterprise
|
||||
? []
|
||||
: [
|
||||
`packages/grafana-api-clients/src/index.ts`,
|
||||
`packages/grafana-api-clients/src/clients/rtkq/index.ts`,
|
||||
`packages/grafana-api-clients/package.json`,
|
||||
]),
|
||||
];
|
||||
};
|
||||
|
||||
@@ -53,9 +65,10 @@ export const runGenerateApis =
|
||||
|
||||
let command;
|
||||
if (isEnterprise) {
|
||||
command = 'yarn process-specs && npx rtk-query-codegen-openapi ./local/generate-enterprise-apis.ts';
|
||||
command =
|
||||
'yarn workspace @grafana/api-clients process-specs && npx rtk-query-codegen-openapi ./local/generate-enterprise-apis.ts';
|
||||
} else {
|
||||
command = 'yarn generate-apis';
|
||||
command = 'yarn workspace @grafana/api-clients generate-apis';
|
||||
}
|
||||
|
||||
console.log(`⏳ Running ${command} to generate endpoints...`);
|
||||
@@ -113,3 +126,62 @@ export const validateGroup = (group: string) => {
|
||||
export const validateVersion = (version: string) => {
|
||||
return version && /^v\d+[a-z]*\d+$/.test(version) ? true : 'Version should be in format: v0alpha1, v1beta2, etc.';
|
||||
};
|
||||
|
||||
export const updatePackageJsonExports =
|
||||
(basePath: string): PlopActionFunction =>
|
||||
(answers) => {
|
||||
try {
|
||||
const { groupName, version } = answers;
|
||||
|
||||
if (!groupName || !version) {
|
||||
return '❌ Missing groupName or version for package.json update';
|
||||
}
|
||||
|
||||
const packageJsonPath = path.join(basePath, 'packages/grafana-api-clients/package.json');
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
|
||||
// Create the new export entry
|
||||
const newExportKey = `./rtkq/${groupName}/${version}`;
|
||||
const newExportValue = {
|
||||
import: `./src/clients/rtkq/${groupName}/${version}/index.ts`,
|
||||
require: `./src/clients/rtkq/${groupName}/${version}/index.ts`,
|
||||
};
|
||||
|
||||
// Check if export already exists
|
||||
if (packageJson.exports[newExportKey]) {
|
||||
return `✅ Export for ${newExportKey} already exists in package.json`;
|
||||
}
|
||||
|
||||
// Add the new export entry
|
||||
packageJson.exports[newExportKey] = newExportValue;
|
||||
|
||||
// Write the updated package.json back to file
|
||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
||||
|
||||
return `✅ Added export for ${newExportKey} to package.json`;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error('❌ Failed to update package.json exports:', errorMessage);
|
||||
return '❌ Failed to update package.json exports. See error above.';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Make this work more generically with endpoints that don't have operationIds defined -
|
||||
* then we can allow selection of endpoints in the generator
|
||||
*/
|
||||
export const getOperationIds = (apiSpec: OpenAPIV3.Document) => {
|
||||
return (
|
||||
Object.values(apiSpec.paths).flatMap((path) => {
|
||||
return Object.entries(path || {})
|
||||
.filter(([name]) => name !== 'parameters')
|
||||
.flatMap(([_, pathItem]) => {
|
||||
if (typeof pathItem === 'object' && 'operationId' in pathItem && pathItem.operationId) {
|
||||
return pathItem.operationId;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter((id) => id !== null);
|
||||
}) || []
|
||||
);
|
||||
};
|
||||
+54
-32
@@ -1,27 +1,26 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import type { NodePlopAPI, PlopGeneratorConfig } from 'plop';
|
||||
|
||||
import {
|
||||
formatEndpoints,
|
||||
validateGroup,
|
||||
validateVersion,
|
||||
formatFiles,
|
||||
getFilesToFormat,
|
||||
runGenerateApis,
|
||||
formatFiles,
|
||||
// The file extension is necessary to make the imports
|
||||
// work with the '--experimental-strip-types' flag
|
||||
// @ts-ignore
|
||||
updatePackageJsonExports,
|
||||
validateGroup,
|
||||
validateVersion,
|
||||
} from './helpers.ts';
|
||||
// @ts-ignore
|
||||
import { type ActionConfig, type PlopData, isPlopData } from './types.ts';
|
||||
|
||||
export default function plopGenerator(plop: NodePlopAPI) {
|
||||
// Grafana root path
|
||||
const basePath = path.resolve(import.meta.dirname, '../..');
|
||||
const basePath = path.resolve(import.meta.dirname, '../../../..');
|
||||
|
||||
// Register custom action types
|
||||
plop.setActionType('runGenerateApis', runGenerateApis(basePath));
|
||||
plop.setActionType('formatFiles', formatFiles(basePath));
|
||||
plop.setActionType('updatePackageJsonExports', updatePackageJsonExports(basePath));
|
||||
|
||||
// Used in templates to format endpoints
|
||||
plop.setHelper('formatEndpoints', formatEndpoints());
|
||||
@@ -29,13 +28,16 @@ export default function plopGenerator(plop: NodePlopAPI) {
|
||||
const generateRtkApiActions = (data: PlopData) => {
|
||||
const { reducerPath, groupName, version, isEnterprise } = data;
|
||||
|
||||
const apiClientBasePath = isEnterprise ? 'public/app/extensions/api/clients' : 'public/app/api/clients';
|
||||
const generateScriptPath = isEnterprise ? 'local/generate-enterprise-apis.ts' : 'scripts/generate-rtk-apis.ts';
|
||||
const apiClientBasePath = isEnterprise
|
||||
? 'public/app/extensions/api/clients'
|
||||
: 'packages/grafana-api-clients/src/clients/rtkq';
|
||||
const generateScriptPath = isEnterprise
|
||||
? 'local/generate-enterprise-apis.ts'
|
||||
: 'packages/grafana-api-clients/src/scripts/generate-rtk-apis.ts';
|
||||
|
||||
// Using app path, so the imports work on any file level
|
||||
const clientImportPath = isEnterprise ? '../extensions/api/clients' : 'app/api/clients';
|
||||
const clientImportPath = isEnterprise ? '../extensions/api/clients' : '.';
|
||||
|
||||
const apiPathPrefix = isEnterprise ? '../public/app/extensions/api/clients' : '../public/app/api/clients';
|
||||
const apiPathPrefix = isEnterprise ? '../public/app/extensions/api/clients' : '../clients';
|
||||
|
||||
const templateData = {
|
||||
...data,
|
||||
@@ -68,27 +70,24 @@ export default function plopGenerator(plop: NodePlopAPI) {
|
||||
actions.push(
|
||||
{
|
||||
type: 'modify',
|
||||
path: path.join(basePath, 'public/app/core/reducers/root.ts'),
|
||||
path: '../clients/rtkq/index.ts',
|
||||
pattern: '// PLOP_INJECT_IMPORT',
|
||||
template: `import { ${reducerPath} } from '${clientImportPath}/${groupName}/${version}';\n// PLOP_INJECT_IMPORT`,
|
||||
template: `import { generatedAPI as ${reducerPath} } from '${clientImportPath}/${groupName}/${version}';\n// PLOP_INJECT_IMPORT`,
|
||||
},
|
||||
{
|
||||
type: 'modify',
|
||||
path: path.join(basePath, 'public/app/core/reducers/root.ts'),
|
||||
path: '../clients/rtkq/index.ts',
|
||||
pattern: '// PLOP_INJECT_REDUCER',
|
||||
template: `[${reducerPath}.reducerPath]: ${reducerPath}.reducer,\n // PLOP_INJECT_REDUCER`,
|
||||
},
|
||||
{
|
||||
type: 'modify',
|
||||
path: path.join(basePath, 'public/app/store/configureStore.ts'),
|
||||
pattern: '// PLOP_INJECT_IMPORT',
|
||||
template: `import { ${reducerPath} } from '${clientImportPath}/${groupName}/${version}';\n// PLOP_INJECT_IMPORT`,
|
||||
},
|
||||
{
|
||||
type: 'modify',
|
||||
path: path.join(basePath, 'public/app/store/configureStore.ts'),
|
||||
path: '../clients/rtkq/index.ts',
|
||||
pattern: '// PLOP_INJECT_MIDDLEWARE',
|
||||
template: `${reducerPath}.middleware,\n // PLOP_INJECT_MIDDLEWARE`,
|
||||
},
|
||||
{
|
||||
type: 'updatePackageJsonExports',
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -108,6 +107,16 @@ export default function plopGenerator(plop: NodePlopAPI) {
|
||||
return actions;
|
||||
};
|
||||
|
||||
// Read files from data/openapi directory and use as an array of API clients that the user can select
|
||||
|
||||
const openapiDir = path.join(basePath, 'data/openapi');
|
||||
let possibleOpenAPISpecs: string[] = [];
|
||||
try {
|
||||
possibleOpenAPISpecs = fs.readdirSync(openapiDir).filter((file: string) => file.endsWith('.json'));
|
||||
} catch (e) {
|
||||
possibleOpenAPISpecs = [];
|
||||
}
|
||||
|
||||
const generator: PlopGeneratorConfig = {
|
||||
description: 'Generate RTK Query API client for a Grafana API group',
|
||||
prompts: [
|
||||
@@ -118,30 +127,43 @@ export default function plopGenerator(plop: NodePlopAPI) {
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'groupName',
|
||||
message: 'API group name (e.g. dashboard):',
|
||||
type: 'list',
|
||||
loop: false,
|
||||
choices: possibleOpenAPISpecs,
|
||||
pageSize: 50,
|
||||
name: 'apiInfo',
|
||||
message: 'OpenAPI spec:',
|
||||
validate: (input: string) => (input?.trim() ? true : 'Group name is required'),
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'groupName',
|
||||
message: 'API group name:',
|
||||
validate: (input: string) => (input?.trim() ? true : 'Group name is required'),
|
||||
default: (answers: { apiInfo?: string }) => answers.apiInfo?.split('.grafana.app-')[0],
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'group',
|
||||
message: 'API group (e.g. dashboard.grafana.app):',
|
||||
message: 'API group:',
|
||||
default: (answers: { groupName?: string }) => `${answers.groupName}.grafana.app`,
|
||||
validate: validateGroup,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'version',
|
||||
message: 'API version (e.g. v0alpha1):',
|
||||
default: 'v0alpha1',
|
||||
message: 'API version:',
|
||||
default: (answers: { apiInfo?: string }) => answers.apiInfo?.split('.grafana.app-')[1].replace(/\.json$/, ''),
|
||||
validate: validateVersion,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'reducerPath',
|
||||
message: 'Reducer path (e.g. dashboardAPIv0alpha1):',
|
||||
default: (answers: { groupName?: string; version?: string }) => `${answers.groupName}API${answers.version}`,
|
||||
message: 'Reducer path:',
|
||||
default: (answers: { groupName?: string; version?: string }) => {
|
||||
const groupNameParsed = answers.groupName?.replace(/\.([a-z])/g, (_, letter) => letter.toUpperCase());
|
||||
return `${groupNameParsed}API${answers.version}`;
|
||||
},
|
||||
validate: (input: string) =>
|
||||
input?.endsWith('API') || input?.match(/API[a-z]\d+[a-z]*\d*$/)
|
||||
? true
|
||||
@@ -150,8 +172,8 @@ export default function plopGenerator(plop: NodePlopAPI) {
|
||||
{
|
||||
type: 'input',
|
||||
name: 'endpoints',
|
||||
default: undefined,
|
||||
message: 'Endpoints to include (comma-separated, optional):',
|
||||
validate: () => true,
|
||||
},
|
||||
],
|
||||
actions: function (data) {
|
||||
@@ -0,0 +1,21 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
{{#if isEnterprise}}
|
||||
import { getAPIBaseURL } from '@grafana/api-clients';
|
||||
import { createBaseQuery } from '@grafana/api-clients/rtkq';
|
||||
{{else}}
|
||||
import { getAPIBaseURL } from '../../../../utils/utils';
|
||||
import { createBaseQuery } from '../../createBaseQuery';
|
||||
{{/if}}
|
||||
|
||||
export const API_GROUP = '{{group}}' as const;
|
||||
export const API_VERSION = '{{version}}' as const;
|
||||
export const BASE_URL = getAPIBaseURL(API_GROUP, API_VERSION);
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: '{{reducerPath}}',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
...createAPIConfig('{{groupName}}', '{{version}}'{{#if endpoints}}, [{{{formatEndpoints endpoints}}}]{{/if}}),
|
||||
// PLOP_INJECT_API_CLIENT - Used by the API client generator
|
||||
@@ -0,0 +1,5 @@
|
||||
export { BASE_URL, API_GROUP, API_VERSION } from './baseAPI';
|
||||
import { generatedAPI as rawAPI } from './endpoints.gen';
|
||||
|
||||
export * from './endpoints.gen';
|
||||
export const generatedAPI = rawAPI.enhanceEndpoints({});
|
||||
+10
-1
@@ -10,8 +10,17 @@ export interface RunGenerateApisActionConfig {
|
||||
isEnterprise: boolean;
|
||||
}
|
||||
|
||||
export interface UpdatePackageJsonExportsActionConfig {
|
||||
type: 'updatePackageJsonExports';
|
||||
}
|
||||
|
||||
// Union type of all possible action configs
|
||||
export type ActionConfig = AddActionConfig | ModifyActionConfig | FormatFilesActionConfig | RunGenerateApisActionConfig;
|
||||
export type ActionConfig =
|
||||
| AddActionConfig
|
||||
| ModifyActionConfig
|
||||
| FormatFilesActionConfig
|
||||
| RunGenerateApisActionConfig
|
||||
| UpdatePackageJsonExportsActionConfig;
|
||||
|
||||
export interface PlopData {
|
||||
groupName: string;
|
||||
@@ -0,0 +1 @@
|
||||
export { getAPINamespace, getAPIBaseURL, normalizeError, handleRequestError } from './utils/utils';
|
||||
@@ -0,0 +1,111 @@
|
||||
// Generates Redux Toolkit API slices for certain APIs from the OpenAPI spec
|
||||
import type { ConfigFile } from '@rtk-query/codegen-openapi';
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
import path from 'path';
|
||||
|
||||
// Grafana root path - navigate up from this script's directory
|
||||
const basePath = path.resolve(__dirname, '../../../..');
|
||||
|
||||
// Include some types that are used inside the @rtk-query/codegen-openapi package
|
||||
// but not exported
|
||||
declare const operationKeys: readonly ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'];
|
||||
type OperationDefinition = {
|
||||
path: string;
|
||||
verb: (typeof operationKeys)[number];
|
||||
pathItem: OpenAPIV3.PathItemObject;
|
||||
operation: OpenAPIV3.OperationObject;
|
||||
};
|
||||
type EndpointMatcher = string[] | ((operationName: string, operationDefinition: OperationDefinition) => boolean);
|
||||
|
||||
/**
|
||||
* Helper to return consistent base API generation config
|
||||
*/
|
||||
const createAPIConfig = (app: string, version: string, filterEndpoints?: EndpointMatcher, additional = {}) => {
|
||||
const filePath = `../clients/rtkq/${app}/${version}/endpoints.gen.ts`;
|
||||
return {
|
||||
[filePath]: {
|
||||
schemaFile: path.join(basePath, `data/openapi/${app}.grafana.app-${version}.json`),
|
||||
apiFile: `../clients/rtkq/${app}/${version}/baseAPI.ts`,
|
||||
filterEndpoints,
|
||||
tag: true,
|
||||
...additional,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const config: ConfigFile = {
|
||||
schemaFile: '', // leave this empty, and instead populate the outputFiles object below
|
||||
apiFile: '', // leave this empty, and instead populate the outputFiles object below
|
||||
exportName: 'generatedAPI',
|
||||
|
||||
outputFiles: {
|
||||
'../clients/rtkq/migrate-to-cloud/endpoints.gen.ts': {
|
||||
schemaFile: path.join(basePath, 'public/openapi3.json'),
|
||||
apiFile: '../clients/rtkq/migrate-to-cloud/baseAPI.ts',
|
||||
hooks: true,
|
||||
filterEndpoints: [
|
||||
'getSessionList',
|
||||
'getSession',
|
||||
'deleteSession',
|
||||
'createSession',
|
||||
|
||||
'getShapshotList',
|
||||
'getSnapshot',
|
||||
'uploadSnapshot',
|
||||
'createSnapshot',
|
||||
'cancelSnapshot',
|
||||
|
||||
'createCloudMigrationToken',
|
||||
'deleteCloudMigrationToken',
|
||||
'getCloudMigrationToken',
|
||||
|
||||
'getDashboardByUid',
|
||||
'getLibraryElementByUid',
|
||||
|
||||
'getResourceDependencies',
|
||||
],
|
||||
},
|
||||
'../clients/rtkq/preferences/user/endpoints.gen.ts': {
|
||||
schemaFile: path.join(basePath, 'public/openapi3.json'),
|
||||
hooks: true,
|
||||
apiFile: '../clients/rtkq/preferences/user/baseAPI.ts',
|
||||
filterEndpoints: ['getUserPreferences', 'updateUserPreferences', 'patchUserPreferences'],
|
||||
},
|
||||
'../clients/rtkq/user/endpoints.gen.ts': {
|
||||
schemaFile: path.join(basePath, 'public/openapi3.json'),
|
||||
hooks: true,
|
||||
apiFile: '../clients/rtkq/user/baseAPI.ts',
|
||||
filterEndpoints: ['starDashboardByUid', 'unstarDashboardByUid'],
|
||||
},
|
||||
...createAPIConfig('iam', 'v0alpha1', ['getDisplayMapping']),
|
||||
...createAPIConfig('provisioning', 'v0alpha1', filterEndpoints, { hooks: true }),
|
||||
...createAPIConfig('folder', 'v1beta1', undefined),
|
||||
...createAPIConfig('advisor', 'v0alpha1', [
|
||||
'createCheck',
|
||||
'getCheck',
|
||||
'listCheck',
|
||||
'deleteCheck',
|
||||
'updateCheck',
|
||||
'listCheckType',
|
||||
'updateCheckType',
|
||||
]),
|
||||
...createAPIConfig('playlist', 'v0alpha1', [
|
||||
'listPlaylist',
|
||||
'getPlaylist',
|
||||
'createPlaylist',
|
||||
'deletePlaylist',
|
||||
'replacePlaylist',
|
||||
]),
|
||||
...createAPIConfig('shorturl', 'v1alpha1'),
|
||||
...createAPIConfig('preferences', 'v1alpha1', undefined, { hooks: true }),
|
||||
...createAPIConfig('dashboard', 'v0alpha1', ['getSearch']),
|
||||
...createAPIConfig('correlations', 'v0alpha1'),
|
||||
// PLOP_INJECT_API_CLIENT - Used by the API client generator
|
||||
},
|
||||
};
|
||||
|
||||
function filterEndpoints(name: string) {
|
||||
return !name.toLowerCase().includes('getapiresources') && !name.toLowerCase().includes('update');
|
||||
}
|
||||
|
||||
export default config;
|
||||
+6
-3
@@ -172,11 +172,14 @@ function processDirectory(sourceDir: string, outputDir: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Grafana root path - navigate up from this script's directory
|
||||
const basePath = path.resolve(import.meta.dirname, '../../../..');
|
||||
|
||||
const sourceDirs = [
|
||||
path.resolve(import.meta.dirname, '../pkg/tests/apis/openapi_snapshots'),
|
||||
path.resolve(import.meta.dirname, '../pkg/extensions/apiserver/tests/openapi_snapshots'),
|
||||
path.join(basePath, 'pkg/tests/apis/openapi_snapshots'),
|
||||
path.join(basePath, 'pkg/extensions/apiserver/tests/openapi_snapshots'),
|
||||
];
|
||||
const outputDir = path.resolve(import.meta.dirname, '../data/openapi');
|
||||
const outputDir = path.join(basePath, 'data/openapi');
|
||||
|
||||
for (const sourceDir of sourceDirs) {
|
||||
processDirectory(sourceDir, outputDir);
|
||||
@@ -0,0 +1,62 @@
|
||||
import { config, isFetchError } from '@grafana/runtime';
|
||||
|
||||
export const getAPINamespace = () => config.namespace;
|
||||
|
||||
/**
|
||||
* Get a base URL for a k8s API endpoint with parameterised namespace given it's group and version
|
||||
* @param group the k8s group, e.g. dashboard.grafana.app
|
||||
* @param version e.g. v0alpha1
|
||||
* @returns
|
||||
*/
|
||||
export const getAPIBaseURL = (group: string, version: string) => {
|
||||
return `/apis/${group}/${version}/namespaces/${getAPINamespace()}` as const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize error from various error formats into a string message
|
||||
* @param e the raw error
|
||||
* @returns string error message
|
||||
*/
|
||||
export const normalizeError = (e: unknown): string => {
|
||||
if (!e) {
|
||||
return 'Unknown error';
|
||||
}
|
||||
|
||||
if (typeof e === 'object') {
|
||||
if ('data' in e && e.data && typeof e.data === 'object' && 'message' in e.data) {
|
||||
return String(e.data.message);
|
||||
}
|
||||
|
||||
// Direct error with message property
|
||||
if ('message' in e) {
|
||||
return String(e.message);
|
||||
}
|
||||
|
||||
// Nested error wrapper (RTK Query format)
|
||||
if ('error' in e) {
|
||||
if (e.error instanceof Error) {
|
||||
return e.error.message;
|
||||
} else if (isFetchError(e.error)) {
|
||||
if (e.error.data && typeof e.error.data === 'object' && 'message' in e.error.data) {
|
||||
return String(e.error.data.message);
|
||||
}
|
||||
if (Array.isArray(e.error.data.errors) && e.error.data.errors.length) {
|
||||
return e.error.data.errors.join('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e instanceof Error) {
|
||||
return e.message;
|
||||
}
|
||||
|
||||
return String(e);
|
||||
};
|
||||
export function handleRequestError(error: unknown) {
|
||||
if (isFetchError(error) || error instanceof Error) {
|
||||
return { error };
|
||||
} else {
|
||||
return { error: new Error('Unknown error') };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["dist", "node_modules", "**/*.test.ts*"]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"extends": "../../scripts/tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"declaration": true,
|
||||
"jsx": "react-jsx",
|
||||
"declarationDir": "./dist/types",
|
||||
"emitDeclarationOnly": true,
|
||||
"isolatedModules": true,
|
||||
"rootDirs": ["."],
|
||||
"allowImportingTsExtensions": true
|
||||
},
|
||||
"exclude": ["dist/**/*"],
|
||||
"include": [
|
||||
"../../public/app/types/window.d.ts",
|
||||
"../../public/app/types/images.d.ts",
|
||||
"../grafana-ui/src/types/*.d.ts",
|
||||
"../grafana-i18n/src/types/*.d.ts",
|
||||
"src/**/*.ts*"
|
||||
]
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
## Generating RTK Query API Clients
|
||||
|
||||
To show the steps to follow, we are going to work on adding an API client to create a new dashboard. Just adapt the following guide to your use case.
|
||||
|
||||
### 1. Generate an OpenAPI snapshot
|
||||
|
||||
First, check if the `group` and the `version` are already present in [openapi_test.go](/pkg/tests/apis/openapi_test.go). If so, move on to the next step.
|
||||
<br/> If you need to add a new block, you can check for the right `group` and `version` in the backend API call that you want to replicate in the frontend.
|
||||
|
||||
```go
|
||||
{
|
||||
Group: "dashboard.grafana.app",
|
||||
Version: "v0alpha1",
|
||||
}
|
||||
```
|
||||
|
||||
Afterwards, you need to run the `TestIntegrationOpenAPIs` test. Note that it will fail the first time you run it. On the second run, it will generate the corresponding OpenAPI spec, which you can find in [openapi_snapshots](/pkg/tests/apis/openapi_snapshots).
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
> Note: You don’t need to follow these two steps if the `group` you’re working with is already in the `openapi_test.go` file.
|
||||
|
||||
### 2. Run the API generator script
|
||||
|
||||
Run `yarn generate:api-client` and follow the prompts. See [API Client Generator](./generator/README.md) for details.
|
||||
@@ -1,14 +0,0 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
import { getAPIBaseURL } from 'app/api/utils';
|
||||
|
||||
export const BASE_URL = getAPIBaseURL('advisor.grafana.app', 'v0alpha1');
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'advisorAPIv0alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
import { generatedAPI } from './endpoints.gen';
|
||||
|
||||
export const advisorAPIv0alpha1 = generatedAPI.enhanceEndpoints({
|
||||
endpoints: {
|
||||
// Need to mutate the generated query to set the Content-Type header correctly
|
||||
updateCheck: (endpointDefinition) => {
|
||||
const originalQuery = endpointDefinition.query;
|
||||
if (originalQuery) {
|
||||
endpointDefinition.query = (requestOptions) => ({
|
||||
...originalQuery(requestOptions),
|
||||
headers: {
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
body: JSON.stringify(requestOptions.patch),
|
||||
});
|
||||
}
|
||||
},
|
||||
updateCheckType: (endpointDefinition) => {
|
||||
const originalQuery = endpointDefinition.query;
|
||||
if (originalQuery) {
|
||||
endpointDefinition.query = (requestOptions) => ({
|
||||
...originalQuery(requestOptions),
|
||||
headers: {
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
body: JSON.stringify(requestOptions.patch),
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
export const {
|
||||
useGetCheckQuery,
|
||||
useListCheckQuery,
|
||||
useCreateCheckMutation,
|
||||
useDeleteCheckMutation,
|
||||
useUpdateCheckMutation,
|
||||
useListCheckTypeQuery,
|
||||
useUpdateCheckTypeMutation,
|
||||
} = advisorAPIv0alpha1;
|
||||
export { type Check, type CheckType } from './endpoints.gen'; // eslint-disable-line
|
||||
@@ -1,14 +0,0 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
import { getAPIBaseURL } from 'app/api/utils';
|
||||
|
||||
export const BASE_URL = getAPIBaseURL('shorturl.grafana.app', 'v1alpha1');
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'shortURLAPIv1alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
import { getAPIBaseURL } from 'app/api/utils';
|
||||
|
||||
export const BASE_URL = getAPIBaseURL('dashboard.grafana.app', 'v0alpha1');
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'dashboardAPIv0alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -1,12 +1,5 @@
|
||||
import { generatedAPI } from './endpoints.gen';
|
||||
import { generatedAPI } from '@grafana/api-clients/rtkq/dashboard/v0alpha1';
|
||||
|
||||
export const dashboardAPIv0alpha1 = generatedAPI.enhanceEndpoints({
|
||||
addTagTypes: ['Folder', 'Dashboard'],
|
||||
endpoints: {
|
||||
getSearch: (endpointDefinition) => {
|
||||
endpointDefinition.providesTags = ['Search', 'Folder', 'Dashboard'];
|
||||
},
|
||||
},
|
||||
});
|
||||
export const dashboardAPIv0alpha1 = generatedAPI.enhanceEndpoints({});
|
||||
|
||||
export const { useGetSearchQuery } = dashboardAPIv0alpha1;
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
import { getAPIBaseURL } from 'app/api/utils';
|
||||
|
||||
export const BASE_URL = getAPIBaseURL('folder.grafana.app', 'v1beta1');
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'folderAPIv1beta1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { generatedAPI } from '@grafana/api-clients/rtkq/folder/v1beta1';
|
||||
import { DescendantCount } from 'app/types/folders';
|
||||
|
||||
import { generatedAPI } from './endpoints.gen';
|
||||
import { getParsedCounts } from './utils';
|
||||
|
||||
export const folderAPIv1beta1 = generatedAPI
|
||||
@@ -80,4 +80,9 @@ export const {
|
||||
} = folderAPIv1beta1;
|
||||
|
||||
// eslint-disable-next-line no-barrel-files/no-barrel-files
|
||||
export { type CreateFolderApiArg, type Folder, type FolderList, type ReplaceFolderApiArg } from './endpoints.gen';
|
||||
export {
|
||||
type Folder,
|
||||
type FolderList,
|
||||
type CreateFolderApiArg,
|
||||
type ReplaceFolderApiArg,
|
||||
} from '@grafana/api-clients/rtkq/folder/v1beta1';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ResourceStats } from '@grafana/api-clients/rtkq/folder/v1beta1';
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { config } from '@grafana/runtime';
|
||||
@@ -6,8 +7,6 @@ import appEvents from '../../../../core/app_events';
|
||||
import { isProvisionedFolder } from '../../../../features/browse-dashboards/api/isProvisioned';
|
||||
import { useDispatch } from '../../../../types/store';
|
||||
|
||||
import { ResourceStats } from './endpoints.gen';
|
||||
|
||||
import { folderAPIv1beta1 as folderAPI } from './index';
|
||||
|
||||
export async function isProvisionedFolderCheck(
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
import { getAPIBaseURL } from 'app/api/utils';
|
||||
|
||||
export const BASE_URL = getAPIBaseURL('iam.grafana.app', 'v0alpha1');
|
||||
|
||||
export const api = createApi({
|
||||
baseQuery: createBaseQuery({ baseURL: BASE_URL }),
|
||||
reducerPath: 'iamAPIv0alpha1',
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import { generatedAPI } from './endpoints.gen';
|
||||
import { generatedAPI } from '@grafana/api-clients/rtkq/iam/v0alpha1';
|
||||
|
||||
export const iamAPIv0alpha1 = generatedAPI.enhanceEndpoints({});
|
||||
|
||||
export const { useGetDisplayMappingQuery, useLazyGetDisplayMappingQuery } = generatedAPI;
|
||||
export const { useGetDisplayMappingQuery, useLazyGetDisplayMappingQuery } = iamAPIv0alpha1;
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
import { getAPIBaseURL } from 'app/api/utils';
|
||||
|
||||
export const BASE_URL = getAPIBaseURL('playlist.grafana.app', 'v0alpha1');
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'playlistAPIv0alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
import { generatedAPI, type Playlist, type PlaylistSpec } from '@grafana/api-clients/rtkq/playlist/v0alpha1';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
|
||||
import { notifyApp } from '../../../../core/actions';
|
||||
@@ -5,8 +6,6 @@ import { createSuccessNotification } from '../../../../core/copy/appNotification
|
||||
import { contextSrv } from '../../../../core/services/context_srv';
|
||||
import { handleError } from '../../../utils';
|
||||
|
||||
import { generatedAPI, Playlist, PlaylistSpec } from './endpoints.gen';
|
||||
|
||||
export const playlistAPIv0alpha1 = generatedAPI.enhanceEndpoints({
|
||||
endpoints: {
|
||||
getPlaylist: {
|
||||
@@ -87,4 +86,4 @@ export const {
|
||||
} = playlistAPIv0alpha1;
|
||||
|
||||
// eslint-disable-next-line no-barrel-files/no-barrel-files
|
||||
export type { Playlist, PlaylistSpec } from './endpoints.gen';
|
||||
export type { Playlist, PlaylistSpec } from '@grafana/api-clients/rtkq/playlist/v0alpha1';
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
import { getAPIBaseURL } from 'app/api/utils';
|
||||
|
||||
export const BASE_URL = getAPIBaseURL('preferences.grafana.app', 'v1alpha1');
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'preferencesAPIv1alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -1,9 +1,8 @@
|
||||
import { generatedAPI } from '@grafana/api-clients/rtkq/preferences/v1alpha1';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import { createSuccessNotification, createErrorNotification } from 'app/core/copy/appNotification';
|
||||
|
||||
import { generatedAPI } from './endpoints.gen';
|
||||
|
||||
export const preferencesAPIv1alpha1 = generatedAPI.enhanceEndpoints({
|
||||
endpoints: {
|
||||
addStar: {
|
||||
@@ -40,4 +39,4 @@ export const preferencesAPIv1alpha1 = generatedAPI.enhanceEndpoints({
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-barrel-files/no-barrel-files
|
||||
export * from './endpoints.gen';
|
||||
export * from '@grafana/api-clients/rtkq/preferences/v1alpha1';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { parseListOptionsSelector } from '../../../../features/apiserver/client';
|
||||
import { ListOptions } from '../../../../features/apiserver/types';
|
||||
import { ListRepositoryApiArg } from '../v0alpha1/endpoints.gen';
|
||||
import { ListRepositoryApiArg } from 'app/api/clients/provisioning/v0alpha1';
|
||||
import { parseListOptionsSelector } from 'app/features/apiserver/client';
|
||||
import { ListOptions } from 'app/features/apiserver/types';
|
||||
|
||||
type ListParams = Omit<ListRepositoryApiArg, 'fieldSelector' | 'labelSelector'> &
|
||||
Pick<ListOptions, 'labelSelector' | 'fieldSelector'>;
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
|
||||
export const BASE_URL = `apis/provisioning.grafana.app/v0alpha1/namespaces/${config.namespace}`;
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'provisioningAPIv0alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -1,3 +1,12 @@
|
||||
import {
|
||||
generatedAPI,
|
||||
type JobSpec,
|
||||
type JobStatus,
|
||||
type RepositorySpec,
|
||||
type RepositoryStatus,
|
||||
type ErrorDetails,
|
||||
type Status,
|
||||
} from '@grafana/api-clients/rtkq/provisioning/v0alpha1';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { isFetchError } from '@grafana/runtime';
|
||||
import { clearFolders } from 'app/features/browse-dashboards/state/slice';
|
||||
@@ -7,18 +16,9 @@ import { notifyApp } from '../../../../core/actions';
|
||||
import { createSuccessNotification, createErrorNotification } from '../../../../core/copy/appNotification';
|
||||
import { PAGE_SIZE } from '../../../../features/browse-dashboards/api/services';
|
||||
import { refetchChildren } from '../../../../features/browse-dashboards/state/actions';
|
||||
import { handleError } from '../../../utils';
|
||||
import { createOnCacheEntryAdded } from '../utils/createOnCacheEntryAdded';
|
||||
|
||||
import {
|
||||
generatedAPI,
|
||||
JobSpec,
|
||||
JobStatus,
|
||||
RepositorySpec,
|
||||
RepositoryStatus,
|
||||
ErrorDetails,
|
||||
Status,
|
||||
} from './endpoints.gen';
|
||||
|
||||
export const provisioningAPIv0alpha1 = generatedAPI.enhanceEndpoints({
|
||||
endpoints: {
|
||||
listJob: {
|
||||
@@ -104,11 +104,8 @@ export const provisioningAPIv0alpha1 = generatedAPI.enhanceEndpoints({
|
||||
try {
|
||||
await queryFulfilled;
|
||||
} catch (e) {
|
||||
if (!e) {
|
||||
dispatch(notifyApp(createErrorNotification('Error validating repository', new Error('Unknown error'))));
|
||||
} else if (e instanceof Error) {
|
||||
dispatch(notifyApp(createErrorNotification('Error validating repository', e)));
|
||||
} else if (typeof e === 'object' && 'error' in e && isFetchError(e.error)) {
|
||||
// Handle special cases first
|
||||
if (typeof e === 'object' && e && 'error' in e && isFetchError(e.error)) {
|
||||
// Handle Status error responses (Kubernetes style)
|
||||
if (e.error.data.kind === 'Status' && e.error.data.status === 'Failure') {
|
||||
const statusError: Status = e.error.data;
|
||||
@@ -120,16 +117,21 @@ export const provisioningAPIv0alpha1 = generatedAPI.enhanceEndpoints({
|
||||
)
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Handle TestResults error responses with field errors
|
||||
else if (Array.isArray(e.error.data.errors) && e.error.data.errors.length) {
|
||||
if (Array.isArray(e.error.data.errors) && e.error.data.errors.length) {
|
||||
const nonFieldErrors = e.error.data.errors.filter((err: ErrorDetails) => !err.field);
|
||||
// Only show notification if there are errors that don't have a field, field errors are handled by the form
|
||||
if (nonFieldErrors.length > 0) {
|
||||
dispatch(notifyApp(createErrorNotification('Error validating repository')));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// For all other cases, use handleError
|
||||
handleError(e, dispatch, 'Error validating repository');
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -242,4 +244,4 @@ export const provisioningAPIv0alpha1 = generatedAPI.enhanceEndpoints({
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-barrel-files/no-barrel-files
|
||||
export * from './endpoints.gen';
|
||||
export * from '@grafana/api-clients/rtkq/provisioning/v0alpha1';
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
import { getAPIBaseURL } from 'app/api/utils';
|
||||
|
||||
export const BASE_URL = getAPIBaseURL('shorturl.grafana.app', 'v1alpha1');
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'shortURLAPIv1alpha1',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { generatedAPI } from './endpoints.gen';
|
||||
import { generatedAPI } from '@grafana/api-clients/rtkq/shorturl/v1alpha1';
|
||||
|
||||
export const shortURLAPIv1alpha1 = generatedAPI.enhanceEndpoints({
|
||||
endpoints: {
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
|
||||
export const legacyUserAPI = createApi({
|
||||
reducerPath: 'legacyUserAPI',
|
||||
baseQuery: createBaseQuery({ baseURL: '/api' }),
|
||||
tagTypes: ['dashboardStars'],
|
||||
endpoints: (build) => ({
|
||||
getStars: build.query<string[], void>({
|
||||
query: () => ({ url: '/user/stars' }),
|
||||
providesTags: ['dashboardStars'],
|
||||
}),
|
||||
starDashboard: build.mutation<void, { id: string }>({
|
||||
query: ({ id }) => ({ url: `/user/stars/dashboard/uid/${id}`, method: 'POST' }),
|
||||
invalidatesTags: ['dashboardStars'],
|
||||
}),
|
||||
unstarDashboard: build.mutation<void, { id: string }>({
|
||||
query: ({ id }) => ({ url: `/user/stars/dashboard/uid/${id}`, method: 'DELETE' }),
|
||||
invalidatesTags: ['dashboardStars'],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useGetStarsQuery, useStarDashboardMutation, useUnstarDashboardMutation } = legacyUserAPI;
|
||||
+8
-26
@@ -1,21 +1,9 @@
|
||||
import { config, isFetchError } from '@grafana/runtime';
|
||||
import { normalizeError } from '@grafana/api-clients';
|
||||
import { ThunkDispatch } from 'app/types/store';
|
||||
|
||||
import { notifyApp } from '../core/actions';
|
||||
import { createErrorNotification } from '../core/copy/appNotification';
|
||||
|
||||
export const getAPINamespace = () => config.namespace;
|
||||
|
||||
/**
|
||||
* Get a base URL for a k8s API endpoint with parameterised namespace given it's group and version
|
||||
* @param group the k8s group, e.g. dashboard.grafana.app
|
||||
* @param version e.g. v0alpha1
|
||||
* @returns
|
||||
*/
|
||||
export const getAPIBaseURL = (group: string, version: string) => {
|
||||
return `/apis/${group}/${version}/namespaces/${getAPINamespace()}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle an error from a k8s API call
|
||||
* @param e the raw error
|
||||
@@ -23,19 +11,8 @@ export const getAPIBaseURL = (group: string, version: string) => {
|
||||
* @param message error alert title. error details will also be surfaced
|
||||
*/
|
||||
export const handleError = (e: unknown, dispatch: ThunkDispatch, message: string) => {
|
||||
if (!e) {
|
||||
dispatch(notifyApp(createErrorNotification(message, new Error('Unknown error'))));
|
||||
} else if (e instanceof Error) {
|
||||
dispatch(notifyApp(createErrorNotification(message, e)));
|
||||
} else if (typeof e === 'object' && 'error' in e) {
|
||||
if (e.error instanceof Error) {
|
||||
dispatch(notifyApp(createErrorNotification(message, e.error)));
|
||||
} else if (isFetchError(e.error)) {
|
||||
if (Array.isArray(e.error.data.errors) && e.error.data.errors.length) {
|
||||
dispatch(notifyApp(createErrorNotification(message, e.error.data.errors.join('\n'))));
|
||||
}
|
||||
}
|
||||
}
|
||||
const errorMessage = normalizeError(e);
|
||||
dispatch(notifyApp(createErrorNotification(message, errorMessage)));
|
||||
};
|
||||
|
||||
export function extractErrorMessage(error: unknown): string {
|
||||
@@ -49,3 +26,8 @@ export function extractErrorMessage(error: unknown): string {
|
||||
}
|
||||
return String(error);
|
||||
}
|
||||
|
||||
// TODO: Change imports to be directly from api-clients package
|
||||
// Best done after hackathon
|
||||
// eslint-disable-next-line no-barrel-files/no-barrel-files
|
||||
export { getAPIBaseURL, getAPINamespace } from '@grafana/api-clients';
|
||||
|
||||
@@ -3,6 +3,7 @@ import { DOMAttributes } from '@react-types/shared';
|
||||
import { memo, forwardRef, useCallback } from 'react';
|
||||
import { useLocation } from 'react-router-dom-v5-compat';
|
||||
|
||||
import { usePatchUserPreferencesMutation } from '@grafana/api-clients/rtkq/legacy/preferences';
|
||||
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t } from '@grafana/i18n';
|
||||
@@ -10,7 +11,6 @@ import { config, reportInteraction } from '@grafana/runtime';
|
||||
import { ScrollContainer, useStyles2 } from '@grafana/ui';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
import { setBookmark } from 'app/core/reducers/navBarTree';
|
||||
import { usePatchUserPreferencesMutation } from 'app/features/preferences/api/index';
|
||||
import { useDispatch, useSelector } from 'app/types/store';
|
||||
|
||||
import { MegaMenuHeader } from './MegaMenuHeader';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { useGetUserPreferencesQuery } from '@grafana/api-clients/rtkq/legacy/preferences';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { useGetUserPreferencesQuery } from 'app/features/preferences/api';
|
||||
|
||||
import { contextSrv } from '../../../services/context_srv';
|
||||
|
||||
|
||||
@@ -2,10 +2,7 @@ import { ReducersMapObject } from '@reduxjs/toolkit';
|
||||
import { AnyAction, combineReducers } from 'redux';
|
||||
|
||||
import { notificationsAPIv0alpha1, rulesAPIv0alpha1 } from '@grafana/alerting/unstable';
|
||||
import { dashboardAPIv0alpha1 } from 'app/api/clients/dashboard/v0alpha1';
|
||||
import { preferencesAPIv1alpha1 } from 'app/api/clients/preferences/v1alpha1';
|
||||
import { shortURLAPIv1alpha1 } from 'app/api/clients/shorturl/v1alpha1';
|
||||
import { legacyUserAPI } from 'app/api/legacy/user/api';
|
||||
import { allReducers as allApiClientReducers } from '@grafana/api-clients/rtkq';
|
||||
import sharedReducers from 'app/core/reducers';
|
||||
import ldapReducers from 'app/features/admin/state/reducers';
|
||||
import alertingReducers from 'app/features/alerting/state/reducers';
|
||||
@@ -20,7 +17,6 @@ import exploreReducers from 'app/features/explore/state/main';
|
||||
import foldersReducers from 'app/features/folders/state/reducers';
|
||||
import invitesReducers from 'app/features/invites/state/reducers';
|
||||
import importDashboardReducers from 'app/features/manage-dashboards/state/reducers';
|
||||
import { cloudMigrationAPI } from 'app/features/migrate-to-cloud/api';
|
||||
import organizationReducers from 'app/features/org/state/reducers';
|
||||
import panelsReducers from 'app/features/panel/state/reducers';
|
||||
import { reducer as pluginsReducer } from 'app/features/plugins/admin/state/reducer';
|
||||
@@ -31,16 +27,8 @@ import teamsReducers from 'app/features/teams/state/reducers';
|
||||
import usersReducers from 'app/features/users/state/reducers';
|
||||
import templatingReducers from 'app/features/variables/state/keyedVariablesReducer';
|
||||
|
||||
import { advisorAPIv0alpha1 } from '../../api/clients/advisor/v0alpha1';
|
||||
import { folderAPIv1beta1 } from '../../api/clients/folder/v1beta1';
|
||||
import { iamAPIv0alpha1 } from '../../api/clients/iam/v0alpha1';
|
||||
import { playlistAPIv0alpha1 } from '../../api/clients/playlist/v0alpha1';
|
||||
import { provisioningAPIv0alpha1 } from '../../api/clients/provisioning/v0alpha1';
|
||||
import { alertingApi } from '../../features/alerting/unified/api/alertingApi';
|
||||
import { userPreferencesAPI } from '../../features/preferences/api';
|
||||
import { cleanUpAction } from '../actions/cleanUp';
|
||||
// Used by the API client generator
|
||||
// PLOP_INJECT_IMPORT
|
||||
|
||||
const rootReducers = {
|
||||
...sharedReducers,
|
||||
@@ -65,23 +53,11 @@ const rootReducers = {
|
||||
...authConfigReducers,
|
||||
plugins: pluginsReducer,
|
||||
[alertingApi.reducerPath]: alertingApi.reducer,
|
||||
[legacyUserAPI.reducerPath]: legacyUserAPI.reducer,
|
||||
[notificationsAPIv0alpha1.reducerPath]: notificationsAPIv0alpha1.reducer,
|
||||
[rulesAPIv0alpha1.reducerPath]: rulesAPIv0alpha1.reducer,
|
||||
[publicDashboardApi.reducerPath]: publicDashboardApi.reducer,
|
||||
[browseDashboardsAPI.reducerPath]: browseDashboardsAPI.reducer,
|
||||
[cloudMigrationAPI.reducerPath]: cloudMigrationAPI.reducer,
|
||||
[iamAPIv0alpha1.reducerPath]: iamAPIv0alpha1.reducer,
|
||||
[playlistAPIv0alpha1.reducerPath]: playlistAPIv0alpha1.reducer,
|
||||
[userPreferencesAPI.reducerPath]: userPreferencesAPI.reducer,
|
||||
[provisioningAPIv0alpha1.reducerPath]: provisioningAPIv0alpha1.reducer,
|
||||
[folderAPIv1beta1.reducerPath]: folderAPIv1beta1.reducer,
|
||||
[advisorAPIv0alpha1.reducerPath]: advisorAPIv0alpha1.reducer,
|
||||
[dashboardAPIv0alpha1.reducerPath]: dashboardAPIv0alpha1.reducer,
|
||||
[shortURLAPIv1alpha1.reducerPath]: shortURLAPIv1alpha1.reducer,
|
||||
[preferencesAPIv1alpha1.reducerPath]: preferencesAPIv1alpha1.reducer,
|
||||
// PLOP_INJECT_REDUCER
|
||||
// Used by the API client generator
|
||||
...allApiClientReducers,
|
||||
};
|
||||
|
||||
const addedReducers = {};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import memoizeOne from 'memoize-one';
|
||||
|
||||
import { generatedAPI } from '@grafana/api-clients/rtkq/shorturl/v1alpha1';
|
||||
import { AbsoluteTimeRange, LogRowModel, UrlQueryMap } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { getBackendSrv, config, locationService } from '@grafana/runtime';
|
||||
@@ -11,7 +12,6 @@ import { getDashboardUrl } from 'app/features/dashboard-scene/utils/getDashboard
|
||||
import { dispatch } from 'app/store/store';
|
||||
|
||||
import { ShortURL } from '../../../../apps/shorturl/plugin/src/generated/shorturl/v1alpha1/shorturl_object_gen';
|
||||
import { generatedAPI } from '../../api/clients/shorturl/v1alpha1/endpoints.gen';
|
||||
import { extractErrorMessage } from '../../api/utils';
|
||||
import { ShareLinkConfiguration } from '../../features/dashboard-scene/sharing/ShareButton/utils';
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { handleRequestError } from '@grafana/api-clients';
|
||||
import { createBaseQuery } from '@grafana/api-clients/rtkq';
|
||||
import { generatedAPI as legacyUserAPI } from '@grafana/api-clients/rtkq/legacy/user';
|
||||
import { AppEvents, locationUtil } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { config, getBackendSrv, isFetchError, locationService } from '@grafana/runtime';
|
||||
import { Dashboard } from '@grafana/schema';
|
||||
import { Spec as DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2';
|
||||
import { isProvisionedFolderCheck } from 'app/api/clients/folder/v1beta1/utils';
|
||||
import { createBaseQuery, handleRequestError } from 'app/api/createBaseQuery';
|
||||
import { legacyUserAPI } from 'app/api/legacy/user/api';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { setStarred } from 'app/core/reducers/navBarTree';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from '@grafana/api-clients/rtkq';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { config, FetchError, isFetchError } from '@grafana/runtime';
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import { createErrorNotification, createSuccessNotification } from 'app/core/copy/appNotification';
|
||||
import {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
## Migrate to Cloud API
|
||||
|
||||
The [`endpoints.gen.ts`](./endpoints.gen.ts) file is machine generated. In order to update it, follow these steps:
|
||||
|
||||
- Run: `make swagger-clean && make openapi3-gen`
|
||||
- Run: `yarn generate-apis`
|
||||
|
||||
If you run into issues, try updating your Node.js version.
|
||||
@@ -1,113 +1,25 @@
|
||||
import { BaseQueryFn, EndpointDefinition } from '@reduxjs/toolkit/query';
|
||||
|
||||
import { handleRequestError } from '@grafana/api-clients';
|
||||
import { generatedAPI } from '@grafana/api-clients/rtkq/legacy/migrate-to-cloud';
|
||||
import { getLocalPlugins } from 'app/features/plugins/admin/api';
|
||||
import { LocalPlugin } from 'app/features/plugins/admin/types';
|
||||
|
||||
import { handleRequestError } from '../../../api/createBaseQuery';
|
||||
|
||||
import { generatedAPI } from './endpoints.gen';
|
||||
|
||||
export * from './endpoints.gen';
|
||||
|
||||
export const cloudMigrationAPI = generatedAPI
|
||||
.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
// Manually written because the Swagger specifications for the plugins endpoint do not exist
|
||||
getLocalPluginList: build.query<LocalPlugin[], void>({
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const list = await getLocalPlugins();
|
||||
return { data: list };
|
||||
} catch (error) {
|
||||
return handleRequestError(error);
|
||||
}
|
||||
},
|
||||
}),
|
||||
export const cloudMigrationAPI = generatedAPI.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
// Manually written because the Swagger specifications for the plugins endpoint do not exist
|
||||
getLocalPluginList: build.query<LocalPlugin[], void>({
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const list = await getLocalPlugins();
|
||||
return { data: list };
|
||||
} catch (error) {
|
||||
return handleRequestError(error);
|
||||
}
|
||||
},
|
||||
}),
|
||||
})
|
||||
.enhanceEndpoints({
|
||||
addTagTypes: [
|
||||
'cloud-migration-token',
|
||||
'cloud-migration-session',
|
||||
'cloud-migration-snapshot',
|
||||
'cloud-migration-resource-dependencies',
|
||||
],
|
||||
|
||||
endpoints: {
|
||||
// Cloud-side - create token
|
||||
getCloudMigrationToken: {
|
||||
providesTags: ['cloud-migration-token'],
|
||||
},
|
||||
createCloudMigrationToken: {
|
||||
invalidatesTags: ['cloud-migration-token'],
|
||||
},
|
||||
deleteCloudMigrationToken: {
|
||||
invalidatesTags: ['cloud-migration-token'],
|
||||
},
|
||||
|
||||
// On-prem session management (entering token)
|
||||
getSessionList: {
|
||||
providesTags: ['cloud-migration-session'] /* should this be a -list? */,
|
||||
},
|
||||
getSession: {
|
||||
providesTags: ['cloud-migration-session'],
|
||||
},
|
||||
createSession: {
|
||||
invalidatesTags: ['cloud-migration-session'],
|
||||
},
|
||||
deleteSession: {
|
||||
invalidatesTags: ['cloud-migration-session', 'cloud-migration-snapshot'],
|
||||
},
|
||||
|
||||
// Snapshot management
|
||||
getShapshotList: {
|
||||
providesTags: ['cloud-migration-snapshot'],
|
||||
},
|
||||
getSnapshot: {
|
||||
providesTags: ['cloud-migration-snapshot'],
|
||||
},
|
||||
createSnapshot: {
|
||||
invalidatesTags: ['cloud-migration-snapshot'],
|
||||
},
|
||||
uploadSnapshot: {
|
||||
invalidatesTags: ['cloud-migration-snapshot'],
|
||||
},
|
||||
|
||||
// Resource dependencies
|
||||
getResourceDependencies: {
|
||||
providesTags: ['cloud-migration-resource-dependencies'],
|
||||
},
|
||||
|
||||
getDashboardByUid: suppressErrorsOnQuery,
|
||||
getLibraryElementByUid: suppressErrorsOnQuery,
|
||||
getLocalPluginList: suppressErrorsOnQuery,
|
||||
},
|
||||
});
|
||||
|
||||
function suppressErrorsOnQuery<
|
||||
QueryArg,
|
||||
BaseQuery extends BaseQueryFn,
|
||||
TagTypes extends string,
|
||||
ResultType,
|
||||
ReducerPath extends string,
|
||||
PageParam,
|
||||
>(endpoint: EndpointDefinition<QueryArg, BaseQuery, TagTypes, ResultType, ReducerPath, PageParam>) {
|
||||
if (!endpoint.query) {
|
||||
return;
|
||||
}
|
||||
|
||||
// internal type from rtk-query that isn't exported
|
||||
type InfiniteQueryCombinedArg<QueryArg, PageParam> = {
|
||||
queryArg: QueryArg;
|
||||
pageParam: PageParam;
|
||||
};
|
||||
|
||||
const originalQuery = endpoint.query;
|
||||
endpoint.query = (arg: QueryArg & InfiniteQueryCombinedArg<QueryArg, PageParam>) => {
|
||||
const baseQuery = originalQuery(arg);
|
||||
baseQuery.showErrorAlert = false;
|
||||
return baseQuery;
|
||||
};
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useGetLocalPluginListQuery } = cloudMigrationAPI;
|
||||
|
||||
// eslint-disable-next-line no-barrel-files/no-barrel-files
|
||||
export * from '@grafana/api-clients/rtkq/legacy/migrate-to-cloud';
|
||||
|
||||
+5
-5
@@ -1,14 +1,14 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { Box, Button, Text } from '@grafana/ui';
|
||||
|
||||
import {
|
||||
useCreateCloudMigrationTokenMutation,
|
||||
useDeleteCloudMigrationTokenMutation,
|
||||
useGetCloudMigrationTokenQuery,
|
||||
} from '../../api';
|
||||
} from '@grafana/api-clients/rtkq/legacy/migrate-to-cloud';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { Box, Button, Text } from '@grafana/ui';
|
||||
|
||||
import { maybeAPIError } from '../../api/errors';
|
||||
import { TokenErrorAlert } from '../TokenErrorAlert';
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@ import { useNavigate } from 'react-router-dom-v5-compat';
|
||||
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { Button, Dropdown, Icon, Menu, Stack } from '@grafana/ui';
|
||||
import { Repository } from 'app/api/clients/provisioning/v0alpha1';
|
||||
import { useGetFrontendSettingsQuery } from 'app/api/clients/provisioning/v0alpha1/endpoints.gen';
|
||||
import { useGetFrontendSettingsQuery, Repository } from 'app/api/clients/provisioning/v0alpha1';
|
||||
|
||||
import { CONNECT_URL, DEFAULT_REPOSITORY_TYPES } from '../constants';
|
||||
import { checkSyncSettings } from '../utils/checkSyncSettings';
|
||||
|
||||
@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
|
||||
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { Card, Stack, Text, useStyles2 } from '@grafana/ui';
|
||||
import { useGetFrontendSettingsQuery } from 'app/api/clients/provisioning/v0alpha1/endpoints.gen';
|
||||
import { useGetFrontendSettingsQuery } from 'app/api/clients/provisioning/v0alpha1';
|
||||
|
||||
import { CONNECT_URL } from '../constants';
|
||||
import { getOrderedRepositoryConfigs } from '../utils/repositoryTypes';
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { getAPIBaseURL } from '@grafana/api-clients';
|
||||
import { Scope, ScopeDashboardBinding, ScopeNode } from '@grafana/data';
|
||||
import { getBackendSrv, config } from '@grafana/runtime';
|
||||
|
||||
import { getAPINamespace } from '../../api/utils';
|
||||
|
||||
import { ScopeNavigation } from './dashboards/types';
|
||||
|
||||
const apiGroup = 'scope.grafana.app';
|
||||
const apiVersion = 'v0alpha1';
|
||||
const apiNamespace = getAPINamespace();
|
||||
const apiUrl = `/apis/${apiGroup}/${apiVersion}/namespaces/${apiNamespace}`;
|
||||
const apiUrl = getAPIBaseURL('scope.grafana.app', 'v0alpha1');
|
||||
|
||||
export class ScopesApiClient {
|
||||
async fetchScope(name: string): Promise<Scope | undefined> {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
import { BASE_URL as v0alphaBaseURL } from '@grafana/api-clients/rtkq/dashboard/v0alpha1';
|
||||
import { generatedAPI as legacyUserAPI } from '@grafana/api-clients/rtkq/legacy/user';
|
||||
import { DataFrame, DataFrameView, getDisplayProcessor, SelectableValue, toDataFrame } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { config, getBackendSrv } from '@grafana/runtime';
|
||||
import { generatedAPI, ListStarsApiResponse } from 'app/api/clients/preferences/v1alpha1';
|
||||
import { legacyUserAPI } from 'app/api/legacy/user/api';
|
||||
import { getAPIBaseURL } from 'app/api/utils';
|
||||
import { TermCount } from 'app/core/components/TagFilter/TagFilter';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
@@ -26,8 +27,7 @@ import { filterSearchResults, replaceCurrentFolderQuery } from './utils';
|
||||
// and that it can not serve any search requests. We are temporarily using the old SQL Search API as a fallback when that happens.
|
||||
const loadingFrameName = 'Loading';
|
||||
|
||||
const baseURL = getAPIBaseURL('dashboard.grafana.app', 'v0alpha1');
|
||||
const searchURI = `${baseURL}/search`;
|
||||
const searchURI = `${v0alphaBaseURL}/search`;
|
||||
|
||||
export type SearchHit = {
|
||||
resource: string; // dashboards | folders
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import {
|
||||
useGetStarsQuery as useLegacyGetStarsQuery,
|
||||
useStarDashboardByUidMutation as useLegacyStarDashboardMutation,
|
||||
useUnstarDashboardByUidMutation as useLegacyUnstarDashboardMutation,
|
||||
} from '@grafana/api-clients/rtkq/legacy/user';
|
||||
import { locationUtil } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { useAddStarMutation, useRemoveStarMutation, useListStarsQuery } from 'app/api/clients/preferences/v1alpha1';
|
||||
import {
|
||||
useGetStarsQuery as useLegacyGetStarsQuery,
|
||||
useStarDashboardMutation as useLegacyStarDashboardMutation,
|
||||
useUnstarDashboardMutation as useLegacyUnstarDashboardMutation,
|
||||
} from 'app/api/legacy/user/api';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { setStarred } from 'app/core/reducers/navBarTree';
|
||||
import { dispatch } from 'app/store/store';
|
||||
@@ -45,9 +45,9 @@ export const useStarItem = (group: string, kind: string) => {
|
||||
|
||||
return async ({ id, title }: StarItemArgs, newStarredState: boolean) => {
|
||||
if (newStarredState) {
|
||||
await addStarLegacy({ id });
|
||||
await addStarLegacy({ dashboardUid: id });
|
||||
} else {
|
||||
await removeStarLegacy({ id });
|
||||
await removeStarLegacy({ dashboardUid: id });
|
||||
}
|
||||
|
||||
updateStarred({ id, title }, newStarredState);
|
||||
|
||||
@@ -3,23 +3,11 @@ import { setupListeners } from '@reduxjs/toolkit/query';
|
||||
import { Middleware } from 'redux';
|
||||
|
||||
import { notificationsAPIv0alpha1, rulesAPIv0alpha1 } from '@grafana/alerting/unstable';
|
||||
import { dashboardAPIv0alpha1 } from 'app/api/clients/dashboard/v0alpha1';
|
||||
import { preferencesAPIv1alpha1 } from 'app/api/clients/preferences/v1alpha1';
|
||||
import { shortURLAPIv1alpha1 } from 'app/api/clients/shorturl/v1alpha1';
|
||||
import { legacyUserAPI } from 'app/api/legacy/user/api';
|
||||
import { allMiddleware as allApiClientMiddleware } from '@grafana/api-clients/rtkq';
|
||||
import { browseDashboardsAPI } from 'app/features/browse-dashboards/api/browseDashboardsAPI';
|
||||
import { publicDashboardApi } from 'app/features/dashboard/api/publicDashboardApi';
|
||||
import { cloudMigrationAPI } from 'app/features/migrate-to-cloud/api';
|
||||
import { userPreferencesAPI } from 'app/features/preferences/api';
|
||||
import { StoreState } from 'app/types/store';
|
||||
|
||||
import { advisorAPIv0alpha1 } from '../api/clients/advisor/v0alpha1';
|
||||
import { folderAPIv1beta1 } from '../api/clients/folder/v1beta1';
|
||||
import { iamAPIv0alpha1 } from '../api/clients/iam/v0alpha1';
|
||||
import { playlistAPIv0alpha1 } from '../api/clients/playlist/v0alpha1';
|
||||
import { provisioningAPIv0alpha1 } from '../api/clients/provisioning/v0alpha1';
|
||||
// Used by the API client generator
|
||||
// PLOP_INJECT_IMPORT
|
||||
import { buildInitialState } from '../core/reducers/navModel';
|
||||
import { addReducer, createRootReducer } from '../core/reducers/root';
|
||||
import { alertingApi } from '../features/alerting/unified/api/alertingApi';
|
||||
@@ -54,19 +42,7 @@ export function configureStore(initialState?: Partial<StoreState>) {
|
||||
// other Grafana core APIs
|
||||
publicDashboardApi.middleware,
|
||||
browseDashboardsAPI.middleware,
|
||||
legacyUserAPI.middleware,
|
||||
cloudMigrationAPI.middleware,
|
||||
userPreferencesAPI.middleware,
|
||||
iamAPIv0alpha1.middleware,
|
||||
playlistAPIv0alpha1.middleware,
|
||||
provisioningAPIv0alpha1.middleware,
|
||||
folderAPIv1beta1.middleware,
|
||||
advisorAPIv0alpha1.middleware,
|
||||
dashboardAPIv0alpha1.middleware,
|
||||
shortURLAPIv1alpha1.middleware,
|
||||
preferencesAPIv1alpha1.middleware,
|
||||
// PLOP_INJECT_MIDDLEWARE
|
||||
// Used by the API client generator
|
||||
...allApiClientMiddleware,
|
||||
...extraMiddleware
|
||||
),
|
||||
devTools: process.env.NODE_ENV !== 'production',
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
// Generates Redux Toolkit API slices for certain APIs from the OpenAPI spec
|
||||
import type { ConfigFile } from '@rtk-query/codegen-openapi';
|
||||
|
||||
const config: ConfigFile = {
|
||||
schemaFile: '', // leave this empty, and instead populate the outputFiles object below
|
||||
apiFile: '', // leave this empty, and instead populate the outputFiles object below
|
||||
exportName: 'generatedAPI',
|
||||
|
||||
outputFiles: {
|
||||
'../public/app/features/migrate-to-cloud/api/endpoints.gen.ts': {
|
||||
schemaFile: '../public/openapi3.json',
|
||||
apiFile: '../public/app/features/migrate-to-cloud/api/baseAPI.ts',
|
||||
apiImport: 'baseAPI',
|
||||
hooks: true,
|
||||
filterEndpoints: [
|
||||
'getSessionList',
|
||||
'getSession',
|
||||
'deleteSession',
|
||||
'createSession',
|
||||
|
||||
'getShapshotList',
|
||||
'getSnapshot',
|
||||
'uploadSnapshot',
|
||||
'createSnapshot',
|
||||
'cancelSnapshot',
|
||||
|
||||
'createCloudMigrationToken',
|
||||
'deleteCloudMigrationToken',
|
||||
'getCloudMigrationToken',
|
||||
|
||||
'getDashboardByUid',
|
||||
'getLibraryElementByUid',
|
||||
|
||||
'getResourceDependencies',
|
||||
],
|
||||
},
|
||||
'../public/app/features/preferences/api/user/endpoints.gen.ts': {
|
||||
schemaFile: '../public/openapi3.json',
|
||||
hooks: true,
|
||||
apiFile: '../public/app/features/preferences/api/user/baseAPI.ts',
|
||||
apiImport: 'baseAPI',
|
||||
filterEndpoints: ['getUserPreferences', 'updateUserPreferences', 'patchUserPreferences'],
|
||||
},
|
||||
'../public/app/api/clients/iam/v0alpha1/endpoints.gen.ts': {
|
||||
schemaFile: '../data/openapi/iam.grafana.app-v0alpha1.json',
|
||||
apiFile: '../public/app/api/clients/iam/v0alpha1/baseAPI.ts',
|
||||
filterEndpoints: ['getDisplayMapping'],
|
||||
tag: true,
|
||||
},
|
||||
'../public/app/api/clients/provisioning/v0alpha1/endpoints.gen.ts': {
|
||||
apiFile: '../public/app/api/clients/provisioning/v0alpha1/baseAPI.ts',
|
||||
schemaFile: '../data/openapi/provisioning.grafana.app-v0alpha1.json',
|
||||
filterEndpoints,
|
||||
tag: true,
|
||||
hooks: true,
|
||||
},
|
||||
'../public/app/api/clients/folder/v1beta1/endpoints.gen.ts': {
|
||||
apiFile: '../public/app/api/clients/folder/v1beta1/baseAPI.ts',
|
||||
schemaFile: '../data/openapi/folder.grafana.app-v1beta1.json',
|
||||
tag: true,
|
||||
},
|
||||
'../public/app/api/clients/advisor/v0alpha1/endpoints.gen.ts': {
|
||||
apiFile: '../public/app/api/clients/advisor/v0alpha1/baseAPI.ts',
|
||||
schemaFile: '../data/openapi/advisor.grafana.app-v0alpha1.json',
|
||||
filterEndpoints: [
|
||||
'createCheck',
|
||||
'getCheck',
|
||||
'listCheck',
|
||||
'deleteCheck',
|
||||
'updateCheck',
|
||||
'listCheckType',
|
||||
'updateCheckType',
|
||||
],
|
||||
tag: true,
|
||||
},
|
||||
'../public/app/api/clients/playlist/v0alpha1/endpoints.gen.ts': {
|
||||
apiFile: '../public/app/api/clients/playlist/v0alpha1/baseAPI.ts',
|
||||
schemaFile: '../data/openapi/playlist.grafana.app-v0alpha1.json',
|
||||
filterEndpoints: ['listPlaylist', 'getPlaylist', 'createPlaylist', 'deletePlaylist', 'replacePlaylist'],
|
||||
tag: true,
|
||||
},
|
||||
'../public/app/api/clients/dashboard/v0alpha1/endpoints.gen.ts': {
|
||||
apiFile: '../public/app/api/clients/dashboard/v0alpha1/baseAPI.ts',
|
||||
schemaFile: '../data/openapi/dashboard.grafana.app-v0alpha1.json',
|
||||
filterEndpoints: [
|
||||
// Do not use any other endpoints from this version
|
||||
// If other endpoints are required, they must be used from a newer version of the dashboard API
|
||||
'getSearch',
|
||||
],
|
||||
tag: true,
|
||||
},
|
||||
'../public/app/api/clients/shorturl/v1alpha1/endpoints.gen.ts': {
|
||||
apiFile: '../public/app/api/clients/shorturl/v1alpha1/baseAPI.ts',
|
||||
schemaFile: '../data/openapi/shorturl.grafana.app-v1alpha1.json',
|
||||
tag: true,
|
||||
},
|
||||
'../public/app/api/clients/correlations/v0alpha1/endpoints.gen.ts': {
|
||||
apiFile: '../public/app/api/clients/correlations/v0alpha1/baseAPI.ts',
|
||||
schemaFile: '../data/openapi/correlations.grafana.app-v0alpha1.json',
|
||||
tag: true,
|
||||
},
|
||||
'../public/app/api/clients/preferences/v1alpha1/endpoints.gen.ts': {
|
||||
apiFile: '../public/app/api/clients/preferences/v1alpha1/baseAPI.ts',
|
||||
schemaFile: '../data/openapi/preferences.grafana.app-v1alpha1.json',
|
||||
tag: true,
|
||||
hooks: true,
|
||||
},
|
||||
// PLOP_INJECT_API_CLIENT - Used by the API client generator
|
||||
},
|
||||
};
|
||||
|
||||
function filterEndpoints(name: string) {
|
||||
return !name.toLowerCase().includes('getapiresources') && !name.toLowerCase().includes('update');
|
||||
}
|
||||
|
||||
export default config;
|
||||
@@ -1,14 +0,0 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { createBaseQuery } from 'app/api/createBaseQuery';
|
||||
import { getAPIBaseURL } from 'app/api/utils';
|
||||
|
||||
export const BASE_URL = getAPIBaseURL('{{group}}', '{{version}}');
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: '{{reducerPath}}',
|
||||
baseQuery: createBaseQuery({
|
||||
baseURL: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user