mirror of https://github.com/grafana/grafana.git
[release-11.6.5] Update npm build in daggerbuild & revert some script changes (#109352)
* update daggerbuild/frontend/npm.go * combine lerna commands * revert scripts changes
This commit is contained in:
parent
2dbf020ca9
commit
2d2d6d1ad5
|
@ -12,22 +12,14 @@ import (
|
|||
// NPMPackages versions and packs the npm packages into tarballs into `npm-packages` directory.
|
||||
// It then returns the npm-packages directory as a dagger.Directory.
|
||||
func NPMPackages(builder *dagger.Container, d *dagger.Client, log *slog.Logger, src *dagger.Directory, ersion string) (*dagger.Directory, error) {
|
||||
// Check if the version of Grafana uses lerna or nx to manage package versioning.
|
||||
var (
|
||||
out = fmt.Sprintf("/src/npm-packages/%%s-%v.tgz", "v"+ersion)
|
||||
|
||||
lernaBuild = fmt.Sprintf("yarn run packages:build && yarn lerna version %s --exact --no-git-tag-version --no-push --force-publish -y", ersion)
|
||||
lernaPack = fmt.Sprintf("yarn lerna exec --no-private -- yarn pack --out %s", out)
|
||||
|
||||
nxBuild = fmt.Sprintf("yarn run packages:build && yarn nx release version %s --no-git-commit --no-git-tag --no-stage-changes --group grafanaPackages", ersion)
|
||||
nxPack = fmt.Sprintf("yarn nx exec --projects=$(cat nx.json | jq -r '.release.groups.grafanaPackages.projects | join(\",\")') -- yarn pack --out %s", out)
|
||||
)
|
||||
|
||||
return builder.WithExec([]string{"mkdir", "npm-packages"}).
|
||||
WithEnvVariable("SHELL", "/bin/bash").
|
||||
WithExec([]string{"yarn", "install", "--immutable"}).
|
||||
WithExec([]string{"/bin/bash", "-c", fmt.Sprintf("if [ -f lerna.json ]; then %s; else %s; fi", lernaBuild, nxBuild)}).
|
||||
WithExec([]string{"/bin/bash", "-c", fmt.Sprintf("if [ -f lerna.json ]; then %s; else %s; fi", lernaPack, nxPack)}).
|
||||
WithExec([]string{"/bin/bash", "-c", fmt.Sprintf("yarn run packages:build && yarn lerna version %s --exact --no-git-tag-version --no-push --force-publish -y && yarn lerna exec --no-private -- yarn pack --out %s", ersion, out)}).
|
||||
Directory("./npm-packages"), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,9 @@ import { writeFile } from 'node:fs/promises';
|
|||
import { resolve } from 'path';
|
||||
|
||||
import { createTheme } from '@grafana/data';
|
||||
|
||||
import { darkThemeVarsTemplate } from './themeTemplates/_variables.dark.scss.tmpl';
|
||||
import { lightThemeVarsTemplate } from './themeTemplates/_variables.light.scss.tmpl';
|
||||
import { commonThemeVarsTemplate } from './themeTemplates/_variables.scss.tmpl';
|
||||
import { darkThemeVarsTemplate } from '@grafana/ui/src/themes/_variables.dark.scss.tmpl';
|
||||
import { lightThemeVarsTemplate } from '@grafana/ui/src/themes/_variables.light.scss.tmpl';
|
||||
import { commonThemeVarsTemplate } from '@grafana/ui/src/themes/_variables.scss.tmpl';
|
||||
|
||||
const darkThemeVariablesPath = resolve(__dirname, 'public', 'sass', '_variables.dark.generated.scss');
|
||||
const lightThemeVariablesPath = resolve(__dirname, 'public', 'sass', '_variables.light.generated.scss');
|
||||
|
|
|
@ -30,8 +30,6 @@ const config: ConfigFile = {
|
|||
|
||||
'getDashboardByUid',
|
||||
'getLibraryElementByUid',
|
||||
|
||||
'getResourceDependencies',
|
||||
],
|
||||
},
|
||||
'../public/app/features/preferences/api/user/endpoints.gen.ts': {
|
||||
|
@ -41,45 +39,25 @@ const config: ConfigFile = {
|
|||
apiImport: 'baseAPI',
|
||||
filterEndpoints: ['getUserPreferences', 'updateUserPreferences', 'patchUserPreferences'],
|
||||
},
|
||||
'../public/app/api/clients/iam/v0alpha1/endpoints.gen.ts': {
|
||||
'../public/app/features/iam/api/endpoints.gen.ts': {
|
||||
schemaFile: '../data/openapi/iam.grafana.app-v0alpha1.json',
|
||||
apiFile: '../public/app/api/clients/iam/v0alpha1/baseAPI.ts',
|
||||
apiFile: '../public/app/features/iam/api/api.ts',
|
||||
apiImport: 'iamApi',
|
||||
filterEndpoints: ['getDisplayMapping'],
|
||||
exportName: 'generatedIamApi',
|
||||
flattenArg: false,
|
||||
tag: true,
|
||||
},
|
||||
'../public/app/api/clients/provisioning/v0alpha1/endpoints.gen.ts': {
|
||||
apiFile: '../public/app/api/clients/provisioning/v0alpha1/baseAPI.ts',
|
||||
'../public/app/features/provisioning/api/endpoints.gen.ts': {
|
||||
apiFile: '../public/app/features/provisioning/api/baseAPI.ts',
|
||||
schemaFile: '../data/openapi/provisioning.grafana.app-v0alpha1.json',
|
||||
apiImport: 'baseAPI',
|
||||
filterEndpoints,
|
||||
argSuffix: 'Arg',
|
||||
responseSuffix: 'Response',
|
||||
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,
|
||||
},
|
||||
// PLOP_INJECT_API_CLIENT - Used by the API client generator
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -5,13 +5,7 @@ content_security_policy_template = """require-trusted-types-for 'script'; script
|
|||
enable_frontend_sandbox_for_plugins = sandbox-app-test,sandbox-test-datasource,sandbox-test-panel
|
||||
|
||||
[feature_toggles]
|
||||
publicDashboards=true
|
||||
grafanaAPIServer=true
|
||||
queryLibrary=true
|
||||
queryService=true
|
||||
|
||||
[environment]
|
||||
stack_id = 12345
|
||||
enable = publicDashboards
|
||||
|
||||
[plugins]
|
||||
allow_loading_unsigned_plugins=grafana-extensionstest-app,grafana-extensionexample1-app,grafana-extensionexample2-app,grafana-extensionexample3-app,grafana-e2etest-datasource
|
||||
|
@ -25,6 +19,3 @@ max_open_conn = 2
|
|||
[smtp]
|
||||
enabled = true
|
||||
host = localhost:7777
|
||||
|
||||
[cloud_migration]
|
||||
developer_mode = true ; Enable developer mode to use in-memory implementations of 3rdparty services needed.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
#!/bin/bash
|
||||
|
||||
. scripts/grafana-server/variables
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
. scripts/grafana-server/variables
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
#!/bin/bash
|
||||
|
||||
DEFAULT_RUNDIR=scripts/grafana-server/tmp
|
||||
RUNDIR=${RUNDIR:-$DEFAULT_RUNDIR}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
. scripts/grafana-server/variables
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import PackageJson from '@npmcli/package-json';
|
||||
import { mkdir } from 'node:fs/promises';
|
||||
import { join, dirname } from 'node:path';
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
||||
|
@ -7,17 +8,18 @@ try {
|
|||
const pkgJson = await PackageJson.load(cwd);
|
||||
const cjsIndex = pkgJson.content.publishConfig?.main ?? pkgJson.content.main;
|
||||
const esmIndex = pkgJson.content.publishConfig?.module ?? pkgJson.content.module;
|
||||
const typesIndex = pkgJson.content.publishConfig?.types ?? pkgJson.content.types;
|
||||
const cjsTypes = pkgJson.content.publishConfig?.types ?? pkgJson.content.types;
|
||||
const esmTypes = `./${join(dirname(esmIndex), 'index.d.mts')}`;
|
||||
|
||||
const exports = {
|
||||
'./package.json': './package.json',
|
||||
'.': {
|
||||
import: {
|
||||
types: typesIndex,
|
||||
types: esmTypes,
|
||||
default: esmIndex,
|
||||
},
|
||||
require: {
|
||||
types: typesIndex,
|
||||
types: cjsTypes,
|
||||
default: cjsIndex,
|
||||
},
|
||||
},
|
||||
|
@ -31,17 +33,9 @@ try {
|
|||
};
|
||||
}
|
||||
|
||||
// Fix for @grafana/i18n so eslint-plugin can be imported by consumers
|
||||
if (pkgJson.content.name === '@grafana/i18n') {
|
||||
exports['./eslint-plugin'] = {
|
||||
import: './dist/eslint/index.cjs',
|
||||
require: './dist/eslint/index.cjs',
|
||||
};
|
||||
}
|
||||
|
||||
pkgJson.update({
|
||||
main: cjsIndex,
|
||||
types: typesIndex,
|
||||
types: cjsTypes,
|
||||
module: esmIndex,
|
||||
exports,
|
||||
});
|
||||
|
@ -58,12 +52,12 @@ try {
|
|||
...pkgJson.content.exports,
|
||||
[`./${aliasName}`]: {
|
||||
import: {
|
||||
types: typesIndex.replace('index', aliasName),
|
||||
types: esmTypes.replace('index', aliasName),
|
||||
default: esmIndex.replace('index', aliasName),
|
||||
},
|
||||
require: {
|
||||
types: typesIndex.replace('index', aliasName),
|
||||
default: cjsIndex.replace('index', aliasName),
|
||||
types: cjsTypes.replace('index', aliasName),
|
||||
default: cjsTypes.replace('index', aliasName),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -86,7 +80,7 @@ async function createAliasPackageJsonFiles(packageJsonContent, aliasName) {
|
|||
const pkgJson = await PackageJson.create(pkgJsonPath, {
|
||||
data: {
|
||||
name: pkgName,
|
||||
types: `../dist/types/${aliasName}.d.ts`,
|
||||
types: `../dist/cjs/${aliasName}.d.cts`,
|
||||
main: `../dist/cjs/${aliasName}.cjs`,
|
||||
module: `../dist/esm/${aliasName}.mjs`,
|
||||
},
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
# RTK Query API Client Generator
|
||||
|
||||
This generator automates the process of creating RTK Query API clients for Grafana's API groups. It replaces the manual steps outlined in the [main API documentation](../../public/app/api/README.md).
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
yarn generate:api-client
|
||||
```
|
||||
|
||||
The CLI will prompt for:
|
||||
|
||||
1. **Enterprise or OSS API** - Whether this is an Enterprise or OSS API. This affects paths and build commands.
|
||||
2. **API group name** - The basic name for the API (e.g., `dashboard`)
|
||||
3. **API group** - The full API group name (defaults to `<group-name>.grafana.app`)
|
||||
4. **API version** - The API version (e.g., `v0alpha1`)
|
||||
5. **Reducer path** - The Redux reducer path (defaults to `<group-name>API`). This will also be used as the API's named export.
|
||||
6. **Endpoints** - Optional comma-separated list of endpoints to include (e.g., `createDashboard,updateDashboard`). If not provided, all endpoints will be included.
|
||||
|
||||
## What It Does
|
||||
|
||||
The generator automates the following:
|
||||
|
||||
1. Creates the `baseAPI.ts` file for the API group
|
||||
2. Updates the appropriate generate script to include the API client
|
||||
- `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
|
||||
5. Formats all generated files using Prettier and ESLint
|
||||
6. Automatically runs the appropriate command to generate endpoints from the OpenAPI schema
|
||||
|
||||
## Limitations
|
||||
|
||||
- The generator is optimized for Kubernetes-style APIs, as it requires Kubernetes resource details. For legacy APIs, manual adjustments may be needed.
|
||||
- It expects processed OpenAPI specifications to exist in the `openapi_snapshots` directory
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Missing OpenAPI Schema
|
||||
|
||||
If an error about a missing OpenAPI schema appears, check that:
|
||||
|
||||
1. The API group and version exist in the backend
|
||||
2. The `TestIntegrationOpenAPIs` test has been run to generate the schema (step 1 in the [main API documentation](../../public/app/api/README.md)).
|
||||
3. The schema file exists at `data/openapi/<group>-<version>.json`
|
||||
|
||||
### Validation Errors
|
||||
|
||||
- API group must include `.grafana.app`
|
||||
- Version must be in format `v0alpha1`, `v1beta2`, etc.
|
||||
- Reducer path must end with `API`
|
|
@ -1,115 +0,0 @@
|
|||
import { execSync } from 'child_process';
|
||||
import path from 'path';
|
||||
|
||||
type PlopActionFunction = (
|
||||
answers: Record<string, unknown>,
|
||||
config?: Record<string, unknown>
|
||||
) => string | Promise<string>;
|
||||
|
||||
// Helper to remove quotes from operation IDs
|
||||
export const removeQuotes = (str: string | unknown) => {
|
||||
if (typeof str !== 'string') {
|
||||
return str;
|
||||
}
|
||||
return str.replace(/^['"](.*)['"]$/, '$1');
|
||||
};
|
||||
|
||||
export const formatEndpoints = () => (endpointsInput: string | string[]) => {
|
||||
if (Array.isArray(endpointsInput)) {
|
||||
return endpointsInput.map((op) => `'${removeQuotes(op)}'`).join(', ');
|
||||
}
|
||||
|
||||
// Handle string input (comma-separated)
|
||||
if (typeof endpointsInput === 'string') {
|
||||
const endpointsArray = endpointsInput
|
||||
.split(',')
|
||||
.map((id) => id.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
return endpointsArray.map((op) => `'${removeQuotes(op)}'`).join(', ');
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
// 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';
|
||||
|
||||
return [
|
||||
`${apiClientBasePath}/${groupName}/${version}/baseAPI.ts`,
|
||||
`${apiClientBasePath}/${groupName}/${version}/index.ts`,
|
||||
generateScriptPath,
|
||||
...(isEnterprise ? [] : [`public/app/core/reducers/root.ts`, `public/app/store/configureStore.ts`]),
|
||||
];
|
||||
};
|
||||
|
||||
export const runGenerateApis =
|
||||
(basePath: string): PlopActionFunction =>
|
||||
(answers, config) => {
|
||||
try {
|
||||
const isEnterprise = answers.isEnterprise || (config && config.isEnterprise);
|
||||
|
||||
let command;
|
||||
if (isEnterprise) {
|
||||
command = 'yarn process-specs && npx rtk-query-codegen-openapi ./local/generate-enterprise-apis.ts';
|
||||
} else {
|
||||
command = 'yarn generate-apis';
|
||||
}
|
||||
|
||||
console.log(`⏳ Running ${command} to generate endpoints...`);
|
||||
execSync(command, { stdio: 'inherit', cwd: basePath });
|
||||
return '✅ API endpoints generated successfully!';
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error('❌ Failed to generate API endpoints:', errorMessage);
|
||||
return '❌ Failed to generate API endpoints. See error above.';
|
||||
}
|
||||
};
|
||||
|
||||
export const formatFiles =
|
||||
(basePath: string): PlopActionFunction =>
|
||||
(_, config) => {
|
||||
if (!config || !Array.isArray(config.files)) {
|
||||
console.error('Invalid config passed to formatFiles action');
|
||||
return '❌ Formatting failed: Invalid configuration';
|
||||
}
|
||||
|
||||
const filesToFormat = config.files.map((file: string) => path.join(basePath, file));
|
||||
|
||||
try {
|
||||
const filesList = filesToFormat.map((file: string) => `"${file}"`).join(' ');
|
||||
|
||||
console.log('🧹 Running ESLint on generated/modified files...');
|
||||
try {
|
||||
execSync(`yarn eslint --fix ${filesList}`, { cwd: basePath });
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`⚠️ Warning: ESLint encountered issues: ${errorMessage}`);
|
||||
}
|
||||
|
||||
console.log('🧹 Running Prettier on generated/modified files...');
|
||||
try {
|
||||
// '--ignore-path' is necessary so the gitignored files ('local/' folder) can still be formatted
|
||||
execSync(`yarn prettier --write ${filesList} --ignore-path=./.prettierignore`, { cwd: basePath });
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`⚠️ Warning: Prettier encountered issues: ${errorMessage}`);
|
||||
}
|
||||
|
||||
return '✅ Files linted and formatted successfully!';
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error('⚠️ Warning: Formatting operations failed:', errorMessage);
|
||||
return '⚠️ Warning: Formatting operations failed.';
|
||||
}
|
||||
};
|
||||
|
||||
export const validateGroup = (group: string) => {
|
||||
return group && group.includes('.grafana.app') ? true : 'Group should be in format: name.grafana.app';
|
||||
};
|
||||
|
||||
export const validateVersion = (version: string) => {
|
||||
return version && /^v\d+[a-z]*\d+$/.test(version) ? true : 'Version should be in format: v0alpha1, v1beta2, etc.';
|
||||
};
|
|
@ -1,167 +0,0 @@
|
|||
import path from 'path';
|
||||
import type { NodePlopAPI, PlopGeneratorConfig } from 'plop';
|
||||
|
||||
import {
|
||||
formatEndpoints,
|
||||
validateGroup,
|
||||
validateVersion,
|
||||
getFilesToFormat,
|
||||
runGenerateApis,
|
||||
formatFiles,
|
||||
// The file extension is necessary to make the imports
|
||||
// work with the '--experimental-strip-types' flag
|
||||
// @ts-ignore
|
||||
} 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, '../..');
|
||||
|
||||
// Register custom action types
|
||||
plop.setActionType('runGenerateApis', runGenerateApis(basePath));
|
||||
plop.setActionType('formatFiles', formatFiles(basePath));
|
||||
|
||||
// Used in templates to format endpoints
|
||||
plop.setHelper('formatEndpoints', formatEndpoints());
|
||||
|
||||
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';
|
||||
|
||||
// Using app path, so the imports work on any file level
|
||||
const clientImportPath = isEnterprise ? '../extensions/api/clients' : 'app/api/clients';
|
||||
|
||||
const apiPathPrefix = isEnterprise ? '../public/app/extensions/api/clients' : '../public/app/api/clients';
|
||||
|
||||
const templateData = {
|
||||
...data,
|
||||
apiPathPrefix,
|
||||
};
|
||||
|
||||
// Base actions that are always added
|
||||
const actions: ActionConfig[] = [
|
||||
{
|
||||
type: 'add',
|
||||
path: path.join(basePath, `${apiClientBasePath}/${groupName}/${version}/baseAPI.ts`),
|
||||
templateFile: './templates/baseAPI.ts.hbs',
|
||||
},
|
||||
{
|
||||
type: 'modify',
|
||||
path: path.join(basePath, generateScriptPath),
|
||||
pattern: '// PLOP_INJECT_API_CLIENT - Used by the API client generator',
|
||||
templateFile: './templates/config-entry.hbs',
|
||||
data: templateData,
|
||||
},
|
||||
{
|
||||
type: 'add',
|
||||
path: path.join(basePath, `${apiClientBasePath}/${groupName}/${version}/index.ts`),
|
||||
templateFile: './templates/index.ts.hbs',
|
||||
},
|
||||
];
|
||||
|
||||
// Only add redux reducer and middleware for OSS clients
|
||||
if (!isEnterprise) {
|
||||
actions.push(
|
||||
{
|
||||
type: 'modify',
|
||||
path: path.join(basePath, 'public/app/core/reducers/root.ts'),
|
||||
pattern: '// PLOP_INJECT_IMPORT',
|
||||
template: `import { ${reducerPath} } from '${clientImportPath}/${groupName}/${version}';\n// PLOP_INJECT_IMPORT`,
|
||||
},
|
||||
{
|
||||
type: 'modify',
|
||||
path: path.join(basePath, 'public/app/core/reducers/root.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'),
|
||||
pattern: '// PLOP_INJECT_MIDDLEWARE',
|
||||
template: `${reducerPath}.middleware,\n // PLOP_INJECT_MIDDLEWARE`,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Add formatting and generation actions
|
||||
actions.push(
|
||||
{
|
||||
type: 'formatFiles',
|
||||
files: getFilesToFormat(groupName, version, isEnterprise),
|
||||
},
|
||||
{
|
||||
type: 'runGenerateApis',
|
||||
isEnterprise,
|
||||
}
|
||||
);
|
||||
|
||||
return actions;
|
||||
};
|
||||
|
||||
const generator: PlopGeneratorConfig = {
|
||||
description: 'Generate RTK Query API client for a Grafana API group',
|
||||
prompts: [
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'isEnterprise',
|
||||
message: 'Is this a Grafana Enterprise API?',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'groupName',
|
||||
message: 'API group name (e.g. dashboard):',
|
||||
validate: (input: string) => (input?.trim() ? true : 'Group name is required'),
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'group',
|
||||
message: 'API group (e.g. dashboard.grafana.app):',
|
||||
default: (answers: { groupName?: string }) => `${answers.groupName}.grafana.app`,
|
||||
validate: validateGroup,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'version',
|
||||
message: 'API version (e.g. v0alpha1):',
|
||||
default: 'v0alpha1',
|
||||
validate: validateVersion,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'reducerPath',
|
||||
message: 'Reducer path (e.g. dashboardAPIv0alpha1):',
|
||||
default: (answers: { groupName?: string; version?: string }) => `${answers.groupName}API${answers.version}`,
|
||||
validate: (input: string) =>
|
||||
input?.endsWith('API') || input?.match(/API[a-z]\d+[a-z]*\d*$/)
|
||||
? true
|
||||
: 'Reducer path should end with "API" or "API<version>" (e.g. dashboardAPI, dashboardAPIv0alpha1)',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'endpoints',
|
||||
message: 'Endpoints to include (comma-separated, optional):',
|
||||
validate: () => true,
|
||||
},
|
||||
],
|
||||
actions: function (data) {
|
||||
if (!isPlopData(data)) {
|
||||
throw new Error('Invalid data format received from prompts');
|
||||
}
|
||||
|
||||
return generateRtkApiActions(data);
|
||||
},
|
||||
};
|
||||
|
||||
plop.setGenerator('rtk-api-client', generator);
|
||||
}
|
|
@ -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: () => ({}),
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
'{{apiPathPrefix}}/{{groupName}}/{{version}}/endpoints.gen.ts': {
|
||||
apiFile: '{{apiPathPrefix}}/{{groupName}}/{{version}}/baseAPI.ts',
|
||||
schemaFile: '../data/openapi/{{group}}-{{version}}.json',
|
||||
{{#if endpoints}}
|
||||
filterEndpoints: [{{{formatEndpoints endpoints}}}],
|
||||
{{/if}}
|
||||
tag: true,
|
||||
},
|
||||
// PLOP_INJECT_API_CLIENT - Used by the API client generator
|
|
@ -1,3 +0,0 @@
|
|||
import { generatedAPI } from './endpoints.gen';
|
||||
|
||||
export const {{reducerPath}} = generatedAPI.enhanceEndpoints({});
|
|
@ -1,27 +0,0 @@
|
|||
import type { AddActionConfig, ModifyActionConfig } from 'plop';
|
||||
|
||||
export interface FormatFilesActionConfig {
|
||||
type: 'formatFiles';
|
||||
files: string[];
|
||||
}
|
||||
|
||||
export interface RunGenerateApisActionConfig {
|
||||
type: 'runGenerateApis';
|
||||
isEnterprise: boolean;
|
||||
}
|
||||
|
||||
// Union type of all possible action configs
|
||||
export type ActionConfig = AddActionConfig | ModifyActionConfig | FormatFilesActionConfig | RunGenerateApisActionConfig;
|
||||
|
||||
export interface PlopData {
|
||||
groupName: string;
|
||||
group: string;
|
||||
version: string;
|
||||
reducerPath: string;
|
||||
endpoints: string;
|
||||
isEnterprise: boolean;
|
||||
}
|
||||
|
||||
export function isPlopData(data: unknown): data is PlopData {
|
||||
return typeof data === 'object' && data !== null;
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
|
@ -69,14 +68,6 @@ module.exports = {
|
|||
new webpack.ProvidePlugin({
|
||||
Buffer: ['buffer', 'Buffer'],
|
||||
}),
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: 'public/img',
|
||||
to: 'img',
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
|
|
|
@ -4,7 +4,6 @@ const browserslist = require('browserslist');
|
|||
const { resolveToEsbuildTarget } = require('esbuild-plugin-browserslist');
|
||||
const ESLintPlugin = require('eslint-webpack-plugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
const fs = require('fs');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const path = require('path');
|
||||
const { DefinePlugin, EnvironmentPlugin } = require('webpack');
|
||||
|
@ -30,21 +29,6 @@ function getDecoupledPlugins() {
|
|||
return packages.filter((pkg) => pkg.dir.includes('plugins/datasource')).map((pkg) => `${pkg.dir}/**`);
|
||||
}
|
||||
|
||||
// When linking scenes for development, resolve the path to the src directory for sourcemaps
|
||||
function scenesModule() {
|
||||
const scenesPath = path.resolve('./node_modules/@grafana/scenes');
|
||||
try {
|
||||
const status = fs.lstatSync(scenesPath);
|
||||
if (status.isSymbolicLink()) {
|
||||
console.log(`scenes is linked to local scenes repo`);
|
||||
return path.resolve(scenesPath + '/src');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error checking scenes path: ${error.message}`);
|
||||
}
|
||||
return scenesPath;
|
||||
}
|
||||
|
||||
const envConfig = getEnvConfig();
|
||||
|
||||
module.exports = (env = {}) => {
|
||||
|
@ -68,10 +52,14 @@ module.exports = (env = {}) => {
|
|||
// Packages linked for development need react to be resolved from the same location
|
||||
react: path.resolve('./node_modules/react'),
|
||||
|
||||
// Also Grafana packages need to be resolved from the same location so they share
|
||||
// the same singletons
|
||||
'@grafana/runtime': path.resolve(__dirname, '../../packages/grafana-runtime'),
|
||||
'@grafana/data': path.resolve(__dirname, '../../packages/grafana-data'),
|
||||
|
||||
// This is required to correctly resolve react-router-dom when linking with
|
||||
// local version of @grafana/scenes
|
||||
'react-router-dom': path.resolve('./node_modules/react-router-dom'),
|
||||
'@grafana/scenes': scenesModule(),
|
||||
},
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in New Issue