Skip to content

Commit f31499d

Browse files
Merge pull request #44 from zalando/chore/setup-local-dev
Setup local development environment
2 parents 2769118 + 5b62d1d commit f31499d

File tree

11 files changed

+1118
-52
lines changed

11 files changed

+1118
-52
lines changed

.gitignore

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ dist-types
2323

2424
# Yarn files
2525
.pnp.*
26-
.yarn/*
27-
!.yarn/patches
28-
!.yarn/plugins
29-
!.yarn/releases
30-
!.yarn/sdks
31-
!.yarn/versions
32-
.yarn-integrity
26+
**/*.yarn/*
27+
!**/*.yarn/patches
28+
!**/*.yarn/plugins
29+
!**/*.yarn/releases
30+
!**/*.yarn/sdks
31+
!**/*.yarn/versions
32+
**/*.yarn-integrity

.releaserc.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
[
1313
"@semantic-release/github",
1414
{
15-
"assets": [
16-
{"path": "dist", "label": "Distribution"}
17-
]
15+
"assets": [{ "path": "dist", "label": "Distribution" }]
1816
}
1917
]
2018
]

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
[![Node.js CI](https://github.com/zalando/backstage-api-linter-plugin/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/zalando/backstage-api-linter-plugin/actions/workflows/node.js.yml)
2-
31
# backstage-plugin-api-linter
42

53
Welcome to the Backstage Plugin API Linter!
@@ -63,6 +61,21 @@ Content
6361

6462
<img src='./docs/linter.png' alt='api-linter screen shot'>
6563

64+
## Running the plugin locally
65+
66+
Follow these steps to run the plugin locally for development:
67+
68+
1. Start Zally locally using Docker.
69+
- Please follow the official Zally instructions here: https://github.com/zalando/zally
70+
2. Install dependencies for the local backend server (it mimics the Backstage backend by proxying requests to Zally):
71+
```bash
72+
yarn --cwd dev install
73+
```
74+
3. Run the development environment (this starts both the frontend and the local backend concurrently):
75+
```bash
76+
yarn dev
77+
```
78+
6679
## How to track user behaviour
6780

6881
We are currently using Google Analytics for tracking the user behavior.

dev/backend.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
const express = require('express');
2+
const expressHttpProxy = require('express-http-proxy');
3+
const cors = require('cors');
4+
5+
const PORT = 8080;
6+
const ZALLY_URL = process.env.ZALLY_URL || 'http://localhost:8000';
7+
8+
const app = express();
9+
10+
app.use(
11+
cors({
12+
origin: (origin, callback) => {
13+
if (!origin) return callback(null, true);
14+
if (origin === 'http://localhost:3000') return callback(null, origin);
15+
return callback(new Error('Not allowed by CORS'));
16+
},
17+
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
18+
allowedHeaders: [
19+
'Content-Type',
20+
'Authorization',
21+
'X-Requested-With',
22+
'Accept',
23+
'Origin',
24+
],
25+
credentials: true,
26+
optionsSuccessStatus: 204,
27+
maxAge: 600,
28+
}),
29+
);
30+
31+
// Basic health check
32+
app.get('/health', (_, res) => res.status(200).send('ok'));
33+
app.get('/api/auth/guest/refresh', (_, res) => res.sendStatus(200));
34+
app.use(
35+
'/api/proxy/api-linter',
36+
// Short-circuit preflight for this route to avoid hitting the proxy
37+
(req, res, next) => {
38+
if (req.method === 'OPTIONS') {
39+
const origin = req.headers.origin || 'http://localhost:3000';
40+
res.set({
41+
'Access-Control-Allow-Origin': origin,
42+
Vary: 'Origin',
43+
'Access-Control-Allow-Credentials': 'true',
44+
'Access-Control-Allow-Methods': 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
45+
'Access-Control-Allow-Headers':
46+
'Content-Type, Authorization, X-Requested-With, Accept, Origin',
47+
'Access-Control-Max-Age': '600',
48+
});
49+
return res.sendStatus(204);
50+
}
51+
return next();
52+
},
53+
expressHttpProxy(ZALLY_URL, {
54+
https: false,
55+
proxyReqPathResolver: req => {
56+
return req.originalUrl.replace(/^\/api\/proxy\/api-linter/, '');
57+
},
58+
proxyErrorHandler: (err, res, next) => {
59+
if (err && (err.code === 'ECONNREFUSED' || err.code === 'ENOTFOUND')) {
60+
// eslint-disable-next-line no-console
61+
console.error(err);
62+
res.status(502).json({ error: 'Bad gateway', detail: err.message });
63+
} else {
64+
next(err);
65+
}
66+
},
67+
parseReqBody: false,
68+
reqAsBuffer: true,
69+
userResHeaderDecorator: (headers, userReq) => {
70+
const origin = userReq.headers.origin;
71+
if (origin) {
72+
// Ensure no wildcard when credentials are used
73+
headers['access-control-allow-origin'] = origin;
74+
headers.vary = 'Origin';
75+
headers['access-control-allow-credentials'] = 'true';
76+
}
77+
if (headers['access-control-allow-origin'] === '*') {
78+
headers['access-control-allow-origin'] =
79+
origin || 'http://localhost:3000';
80+
}
81+
return headers;
82+
},
83+
userResDecorator: (proxyRes, proxyResData, userReq) => {
84+
// eslint-disable-next-line no-console
85+
console.log(
86+
`[proxy] ${userReq.method} ${userReq.originalUrl} -> ${proxyRes.statusCode}`,
87+
);
88+
return proxyResData;
89+
},
90+
}),
91+
);
92+
93+
const server = app.listen(PORT, () => {
94+
// eslint-disable-next-line no-console
95+
console.log(`Backend proxy listening on http://localhost:${PORT}`);
96+
});
97+
98+
const shutdown = signal => {
99+
// eslint-disable-next-line no-console
100+
console.log(`\n${signal} received. Shutting down...`);
101+
server.close(() => process.exit(0));
102+
};
103+
104+
['SIGINT', 'SIGTERM'].forEach(sig => process.on(sig, () => shutdown(sig)));

dev/index.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
// eslint-disable-next-line import/no-extraneous-dependencies
12
import React from 'react';
3+
// eslint-disable-next-line import/no-extraneous-dependencies
24
import { createDevApp } from '@backstage/dev-utils';
35
import { APILinterPlugin } from '../src/plugin';
46
import { APILinter } from '../src';
57

68
createDevApp()
79
.registerPlugin(APILinterPlugin)
810
.addPage({
9-
element: <APILinter />,
11+
element: (
12+
<div style={{ padding: '1rem' }}>
13+
<APILinter />
14+
</div>
15+
),
1016
title: 'Root Page',
1117
path: '/api-linter',
1218
})

dev/package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "backend",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"scripts": {
6+
"start": "node backend.js"
7+
},
8+
"keywords": [],
9+
"author": "",
10+
"license": "ISC",
11+
"type": "commonjs",
12+
"description": "",
13+
"dependencies": {
14+
"cors": "^2.8.5",
15+
"express": "^5.1.0",
16+
"express-http-proxy": "^2.1.2"
17+
},
18+
"devDependencies": {
19+
"@types/cors": "^2.8.19",
20+
"@types/express": "^5.0.3",
21+
"@types/express-http-proxy": "^1.6.7"
22+
}
23+
}

0 commit comments

Comments
 (0)