Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["@salesforce/b2c-plugin-example-config"]
"ignore": ["@salesforce/b2c-plugin-example-config", "b2c-grafana-datasource"]
}
5 changes: 5 additions & 0 deletions .changeset/grafana-datasource-and-go-sdk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@salesforce/b2c-tooling-sdk': minor
---

Metrics tag enrichment (`parseSeriesTags`/`enrichMetricsTags`) is now driven by a declarative rule catalog exported to `specs/metrics-tags-catalog.json`, with a golden fixture (`specs/metrics-tags.golden.json`) that pins the expected tag output. This is the shared source of truth consumed by the new Go SDK and Grafana datasource, and guards against parser drift. No API changes — existing callers are unaffected.
43 changes: 43 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,46 @@ jobs:
packages/b2c-tooling-sdk/coverage/
packages/b2c-cli/coverage/
retention-days: 30

test-go:
runs-on: ubuntu-latest

strategy:
matrix:
go-version: ['1.26.x']

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Setup Go ${{ matrix.go-version }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
cache-dependency-path: |
packages/b2c-tooling-sdk-go/go.sum
packages/b2c-grafana-datasource/go.sum

- name: Go SDK - Build
working-directory: packages/b2c-tooling-sdk-go
run: go build ./...

- name: Go SDK - Vet
working-directory: packages/b2c-tooling-sdk-go
run: go vet ./...

- name: Go SDK - Test
working-directory: packages/b2c-tooling-sdk-go
run: go test ./... -v

- name: Grafana Plugin - Build
working-directory: packages/b2c-grafana-datasource
run: go build ./...

- name: Grafana Plugin - Vet
working-directory: packages/b2c-grafana-datasource
run: go vet ./...

- name: Grafana Plugin - Test
working-directory: packages/b2c-grafana-datasource
run: go test ./... -v
25 changes: 25 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ This is a monorepo project with the following packages:
- `./packages/b2c-tooling-sdk` - the SDK/library for B2C Commerce operations; supports the CLI and can be used standalone
- `./packages/b2c-dx-mcp` - Model Context Protocol server; also built with oclif
- `./packages/b2c-vs-extension` - VS Code extension (not published to npm; packaged as VSIX and versioned via git tags)
- `./packages/b2c-grafana-datasource` - Grafana datasource plugins (Metrics + CIP) with Go backend; versioned via git tags
- `./packages/b2c-tooling-sdk-go` - Go SDK for Metrics API and CIP; standalone, used by Grafana plugins; versioned via git tags
- `./docs` - documentation site (private `@salesforce/b2c-dx-docs` workspace package; not published to npm)

## Common Commands
Expand All @@ -26,6 +28,29 @@ pnpm --filter @salesforce/b2c-cli run dev
./cli
```

## Grafana Plugin / Go SDK Development

The Grafana plugin and Go SDK are separate from the Node.js/TypeScript packages:

```bash
# Build Grafana plugin (both datasources)
cd packages/b2c-grafana-datasource
npm run build # Frontend + both backend binaries
go build -o dist/gpx_b2c_metrics ./pkg
go build -o dist/gpx_b2c_cip ./pkg/cip

# Test Go SDK
cd packages/b2c-tooling-sdk-go
go test ./...
go test -cover ./...

# Run Grafana demo (Docker-based)
cd packages/b2c-grafana-datasource
make demo # Mock data, no credentials
make real INSTANCE=bdpx-prd # Live B2C tenant via b2c CLI
make down # Stop demo
```

## Commands for Coding Agents

These commands produce condensed output optimized for AI coding agents:
Expand Down
57 changes: 57 additions & 0 deletions packages/b2c-grafana-datasource/.config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Webpack Build Configuration

This directory contains the webpack configuration for building the Grafana datasource frontend plugin.

## Files

- **webpack.config.ts** - Main webpack configuration that produces AMD module format for Grafana

## Build Output

The webpack build produces the following in `dist/`:
- `module.js` - AMD module loadable by Grafana's SystemJS runtime
- `module.js.map` - Source map for debugging
- `plugin.json` - Plugin metadata (copied from src/)
- `img/logo.svg` - Plugin logo (copied from src/img/)

## Key Configuration Details

### AMD Module Format
Grafana uses SystemJS to load plugins, which requires AMD module format:
```typescript
output: {
libraryTarget: 'amd'
}
```

### Externals
These packages are provided by Grafana at runtime and must not be bundled:
- `@grafana/data`, `@grafana/ui`, `@grafana/runtime`
- `react`, `react-dom`
- `@emotion/*` (used internally by Grafana)
- `lodash`

### Build Tool: SWC
Uses `swc-loader` instead of `ts-loader` for faster TypeScript compilation.

## Commands

```bash
# Production build
npm run build

# Development build with watch mode
npm run dev

# Type check only
npm run typecheck
```

## Docker Integration

The Docker build stage runs:
```dockerfile
RUN npm install --production=false && npm run build
```

This produces a complete plugin directory ready to mount into Grafana.
122 changes: 122 additions & 0 deletions packages/b2c-grafana-datasource/.config/webpack.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2025, Salesforce, Inc.
* SPDX-License-Identifier: Apache-2
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
*/

import CopyWebpackPlugin from 'copy-webpack-plugin';
import path from 'path';
import { fileURLToPath } from 'url';
import { Configuration } from 'webpack';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const config = (env: any): Configuration => {
const isProduction = env.production === true;

return {
mode: isProduction ? 'production' : 'development',
target: 'web',

entry: {
// Metrics datasource → dist/module.js
module: './src/module.ts',
// CIP datasource → dist/cip/module.js (separate plugin dir)
'cip/module': './src/cip/module.ts',
},

output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].js',
libraryTarget: 'amd', // Grafana uses AMD/SystemJS module format
clean: true, // Clean dist folder before build
},

// Externals - Grafana provides these at runtime
externals: [
'lodash',
'react',
'react-dom',
'@grafana/data',
'@grafana/ui',
'@grafana/runtime',
({ request }, callback) => {
// Externalize all @grafana/* packages
if (request && request.startsWith('@grafana/')) {
return callback(null, request);
}
// Externalize all @emotion/* packages (used by @grafana/ui)
if (request && request.startsWith('@emotion/')) {
return callback(null, request);
}
callback();
},
],

resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
// Grafana 10+ uses ESM, but we still need to resolve node modules
alias: {
// Ensure we're using the same React instance as Grafana
react: path.resolve(__dirname, '../node_modules/react'),
'react-dom': path.resolve(__dirname, '../node_modules/react-dom'),
},
},

module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: {
loader: 'swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
target: 'es2020',
transform: {
react: {
runtime: 'automatic',
development: !isProduction,
},
},
},
},
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset/resource',
},
],
},

plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: 'src/plugin.json', to: '.' },
{ from: 'src/img', to: 'img', noErrorOnMissing: true },
// CIP datasource assets → dist/cip/
{ from: 'src/cip/plugin.json', to: 'cip' },
{ from: 'src/cip/img', to: 'cip/img', noErrorOnMissing: true },
],
}),
],

devtool: isProduction ? 'source-map' : 'eval-source-map',

performance: {
hints: false,
},
};
};

export default config;
21 changes: 21 additions & 0 deletions packages/b2c-grafana-datasource/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Exclude everything except what we need
*

# Allow the two packages we need
!b2c-grafana-datasource/
!b2c-tooling-sdk-go/

# Exclude build artifacts and dependencies
b2c-grafana-datasource/node_modules/
b2c-grafana-datasource/dist/
b2c-grafana-datasource/.git/
b2c-tooling-sdk-go/.git/

# Exclude other packages (if building from packages/ dir)
b2c-cli/
b2c-tooling-sdk/
b2c-dx-mcp/
b2c-vs-extension/
b2c-agent-plugins/
mrt-utilities/
.git/
11 changes: 11 additions & 0 deletions packages/b2c-grafana-datasource/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": ["@grafana/eslint-config"],
"root": true,
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"rules": {
"react/prop-types": "off"
}
}
24 changes: 24 additions & 0 deletions packages/b2c-grafana-datasource/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Build outputs
dist/

# Go binaries
*.exe
*.dll
*.so
*.dylib
*.test
*.out
go.work

# Demo binaries
demo/mock-metrics/mock-metrics

# Node.js
node_modules/
*.log
*.boot.log
.DS_Store

# IDE
.vscode/
.idea/
Loading
Loading