Skip to content

Commit 32f00a8

Browse files
Migrate to proxy architecture with lazy loading and add CI (#2)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 7e63ad9 commit 32f00a8

File tree

21 files changed

+16343
-613
lines changed

21 files changed

+16343
-613
lines changed

.devcontainer/devcontainer.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "Flagsmith Backstage Plugin",
3+
"image": "mcr.microsoft.com/devcontainers/typescript-node:22",
4+
"postCreateCommand": "yarn install",
5+
"forwardPorts": [3000],
6+
"portsAttributes": {
7+
"3000": {
8+
"label": "Plugin Preview",
9+
"onAutoForward": "openBrowser"
10+
}
11+
},
12+
"customizations": {
13+
"vscode": {
14+
"extensions": [
15+
"esbenp.prettier-vscode",
16+
"dbaeumer.vscode-eslint"
17+
],
18+
"settings": {
19+
"editor.formatOnSave": true
20+
}
21+
}
22+
},
23+
"postStartCommand": "echo '🚀 Run: yarn start' && echo '📍 Then open the forwarded port 3000'"
24+
}

.github/workflows/ci.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
lint:
11+
name: Lint
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
17+
- name: Setup Node.js
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: '22'
21+
cache: 'yarn'
22+
23+
- name: Install dependencies
24+
run: yarn install --frozen-lockfile
25+
26+
- name: Run lint
27+
run: yarn lint
28+
29+
typecheck:
30+
name: TypeScript
31+
runs-on: ubuntu-latest
32+
steps:
33+
- name: Checkout code
34+
uses: actions/checkout@v4
35+
36+
- name: Setup Node.js
37+
uses: actions/setup-node@v4
38+
with:
39+
node-version: '22'
40+
cache: 'yarn'
41+
42+
- name: Install dependencies
43+
run: yarn install --frozen-lockfile
44+
45+
- name: TypeScript check
46+
run: yarn tsc
47+
48+
build:
49+
name: Build
50+
runs-on: ubuntu-latest
51+
steps:
52+
- name: Checkout code
53+
uses: actions/checkout@v4
54+
55+
- name: Setup Node.js
56+
uses: actions/setup-node@v4
57+
with:
58+
node-version: '22'
59+
cache: 'yarn'
60+
61+
- name: Install dependencies
62+
run: yarn install --frozen-lockfile
63+
64+
- name: Build package
65+
run: yarn build:all

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ node_modules/
99
# Production
1010
/build
1111
/dist
12+
/dist-demo
13+
/dist-types
1214
/lib
1315
*.tsbuildinfo
1416

1517
# Misc
1618
.DS_Store
1719
.env
20+
app-config.local.yaml
1821
.env.local
1922
.env.development.local
2023
.env.test.local

.husky/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
npx lint-staged

README.md

Lines changed: 148 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,161 @@
1-
# flagsmith
1+
# Flagsmith Plugin for Backstage
22

3-
Welcome to the Flagsmith plugin!
3+
Integrate [Flagsmith](https://flagsmith.com) feature flags into your Backstage instance.
44

5-
This plugins:
5+
## Features
66

7-
- Adds a 'Feature Flags' tab on component pages.
8-
- Provides 2 Cards that can be added to component Overview pages.
7+
- **Feature Flags Tab** - View all feature flags for a service directly in the entity page
8+
- **Overview Card** - Quick summary of flags and their states
9+
- **Usage Card** - Display Flagsmith usage metrics
910

10-
## Getting started
11+
## Installation
1112

12-
Currently, it is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
13+
### 1. Install the plugin
1314

14-
Add the following annotations to a component to link it to a flagsmith project, replacing with your Project and Organization IDs:
15+
```bash
16+
# From your Backstage root directory
17+
yarn --cwd packages/app add @flagsmith/backstage-plugin
18+
```
19+
20+
### 2. Configure the Backstage proxy
21+
22+
Add to your `app-config.yaml` (or `app-config.local.yaml` for local development):
23+
24+
```yaml
25+
proxy:
26+
endpoints:
27+
'/flagsmith':
28+
target: 'https://api.flagsmith.com/api/v1'
29+
headers:
30+
Authorization: Api-Key ${FLAGSMITH_API_TOKEN}
31+
```
1532
33+
> **Note:** Use an environment variable for the API token in production. Never commit tokens to version control.
34+
35+
For self-hosted Flagsmith, change the target URL:
36+
37+
```yaml
38+
proxy:
39+
endpoints:
40+
'/flagsmith':
41+
target: 'https://your-flagsmith-instance.com/api/v1'
42+
headers:
43+
Authorization: Api-Key ${FLAGSMITH_API_TOKEN}
1644
```
17-
annotations:
18-
flagsmith.com/project-id: "00000"
19-
flagsmith.com/org-id: "00000" # Optional, defaults to first org
45+
46+
### 3. Add the Feature Flags tab to entity pages
47+
48+
In `packages/app/src/components/catalog/EntityPage.tsx`:
49+
50+
```typescript
51+
import { FlagsTab } from '@flagsmith/backstage-plugin';
52+
53+
// Add to your entity page layout (e.g., serviceEntityPage)
54+
<EntityLayout.Route path="/feature-flags" title="Feature Flags">
55+
<FlagsTab />
56+
</EntityLayout.Route>
57+
```
58+
59+
### 4. (Optional) Add cards to the Overview page
60+
61+
```typescript
62+
import {
63+
FlagsmithOverviewCard,
64+
FlagsmithUsageCard,
65+
} from '@flagsmith/backstage-plugin';
66+
67+
// Add to your entity overview page
68+
<Grid item md={6}>
69+
<FlagsmithOverviewCard />
70+
</Grid>
71+
<Grid item md={6}>
72+
<FlagsmithUsageCard />
73+
</Grid>
74+
```
75+
76+
### 5. Annotate your entities
77+
78+
Add Flagsmith annotations to your `catalog-info.yaml`:
79+
80+
```yaml
81+
apiVersion: backstage.io/v1alpha1
82+
kind: Component
83+
metadata:
84+
name: my-service
85+
annotations:
86+
flagsmith.com/project-id: '12345'
87+
flagsmith.com/org-id: '67890' # Optional - defaults to first organization
88+
spec:
89+
type: service
90+
owner: team-a
2091
```
2192

22-
Configure your credentials by adding the following to app-config.yaml (or your local override app-config.local.yaml):
93+
## Getting your Flagsmith credentials
94+
95+
1. Log in to your [Flagsmith dashboard](https://app.flagsmith.com)
96+
2. Go to **Organisation Settings** > **API Keys**
97+
3. Create or copy your **Admin API Key**
98+
4. Find your **Project ID** and **Organisation ID** in the URL or project settings
99+
100+
## Development
101+
102+
### Prerequisites
103+
104+
- Node.js 22+ (Node 24 has known ESM compatibility issues with Backstage)
105+
- Yarn
106+
- A Backstage application for testing
107+
108+
### Local Development Setup
109+
110+
1. Clone the repository:
111+
112+
```bash
113+
git clone https://github.com/Flagsmith/flagsmith-backstage-plugin.git
114+
cd flagsmith-backstage-plugin
115+
```
116+
117+
2. Install dependencies:
118+
119+
```bash
120+
yarn install
121+
```
122+
123+
3. To test in a Backstage app, copy or link the plugin to your Backstage workspace's `plugins/` directory and add it as a workspace dependency.
124+
125+
4. Create `app-config.local.yaml` with your Flagsmith credentials (this file is gitignored).
126+
127+
5. Run the Backstage app:
128+
```bash
129+
yarn start
130+
```
131+
132+
### Available Scripts
133+
134+
| Command | Description |
135+
| ------------ | ---------------------------- |
136+
| `yarn start` | Start the development server |
137+
| `yarn build` | Build for production |
138+
| `yarn test` | Run tests |
139+
| `yarn lint` | Lint the codebase |
140+
141+
### Project Structure
23142

24143
```
25-
# Backstage override configuration for your local development environment
26-
flagsmith:
27-
apiUrl: https://api.flagsmith.com
28-
apiToken: yourApiToken
144+
src/
145+
├── components/ # React components
146+
│ ├── FlagsTab.tsx
147+
│ ├── FlagsmithOverviewCard.tsx
148+
│ └── FlagsmithUsageCard.tsx
149+
├── api/ # API client (uses Backstage proxy)
150+
│ └── FlagsmithClient.ts
151+
├── plugin.ts # Frontend plugin definition
152+
└── index.ts # Package exports
29153
```
154+
155+
## Contributing
156+
157+
Contributions are welcome! Please open an issue or submit a pull request.
158+
159+
## License
160+
161+
Apache-2.0

app-config.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
app:
2+
title: Flagsmith Plugin Dev
3+
baseUrl: http://localhost:3000
4+
5+
backend:
6+
baseUrl: http://localhost:7007
7+
listen:
8+
port: 7007
9+
10+
# Mock proxy endpoint - requests are intercepted by MSW
11+
proxy:
12+
endpoints:
13+
'/flagsmith':
14+
target: 'http://localhost:7007'

dev/index.tsx

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,66 @@
11
import { createDevApp } from '@backstage/dev-utils';
2-
import { flagsmithPlugin } from '../src/plugin';
2+
import { EntityProvider } from '@backstage/plugin-catalog-react';
3+
import { Entity } from '@backstage/catalog-model';
4+
import { setupWorker } from 'msw';
5+
import { PropsWithChildren } from 'react';
6+
import { flagsmithPlugin, FlagsTab, FlagsmithOverviewCard, FlagsmithUsageCard } from '../src';
7+
import { handlers } from './mockHandlers';
8+
9+
// Start MSW worker for API mocking
10+
const worker = setupWorker(...handlers);
11+
worker.start({
12+
onUnhandledRequest: 'bypass',
13+
});
14+
15+
// Mock entity with Flagsmith annotations
16+
const mockEntity: Entity = {
17+
apiVersion: 'backstage.io/v1alpha1',
18+
kind: 'Component',
19+
metadata: {
20+
name: 'demo-service',
21+
description: 'A demo service with Flagsmith feature flags integration',
22+
annotations: {
23+
'flagsmith.com/project-id': '31465',
24+
'flagsmith.com/org-id': '24242',
25+
},
26+
},
27+
spec: {
28+
type: 'service',
29+
lifecycle: 'production',
30+
owner: 'guests',
31+
},
32+
};
33+
34+
// Wrapper component to provide entity context
35+
const EntityWrapper = ({ children }: PropsWithChildren<{}>) => (
36+
<EntityProvider entity={mockEntity}>{children}</EntityProvider>
37+
);
338

439
createDevApp()
540
.registerPlugin(flagsmithPlugin)
41+
.addPage({
42+
element: (
43+
<EntityWrapper>
44+
<FlagsTab />
45+
</EntityWrapper>
46+
),
47+
title: 'Feature Flags',
48+
path: '/flagsmith',
49+
})
50+
.addPage({
51+
element: (
52+
<EntityWrapper>
53+
<div style={{ padding: 20, display: 'flex', gap: 20, flexWrap: 'wrap' }}>
54+
<div style={{ flex: '1 1 400px', maxWidth: 600 }}>
55+
<FlagsmithOverviewCard />
56+
</div>
57+
<div style={{ flex: '1 1 400px', maxWidth: 600 }}>
58+
<FlagsmithUsageCard />
59+
</div>
60+
</div>
61+
</EntityWrapper>
62+
),
63+
title: 'Overview Cards',
64+
path: '/flagsmith-cards',
65+
})
666
.render();

0 commit comments

Comments
 (0)