Implement APIs of sonarqube-backend plugin's router
Also update the standalone server to add router dependencies Signed-off-by: Neemys <36508659+Neemys@users.noreply.github.com>
This commit is contained in:
@@ -19,13 +19,23 @@ import express from 'express';
|
||||
import request from 'supertest';
|
||||
|
||||
import { createRouter } from './router';
|
||||
import { SonarqubeFindings } from './sonarqubeInfoProvider';
|
||||
|
||||
describe('createRouter', () => {
|
||||
let app: express.Express;
|
||||
const getBaseUrlMock: jest.Mock<string, [string]> = jest.fn();
|
||||
const getFindingsMock: jest.Mock<
|
||||
Promise<SonarqubeFindings | undefined>,
|
||||
[string, string]
|
||||
> = jest.fn();
|
||||
|
||||
beforeAll(async () => {
|
||||
const router = await createRouter({
|
||||
logger: getVoidLogger(),
|
||||
sonarqubeInfoProvider: {
|
||||
getBaseUrl: getBaseUrlMock,
|
||||
getFindings: getFindingsMock,
|
||||
},
|
||||
});
|
||||
app = express().use(router);
|
||||
});
|
||||
@@ -35,17 +45,76 @@ describe('createRouter', () => {
|
||||
});
|
||||
|
||||
describe('GET /findings', () => {
|
||||
const DUMMY_COMPONENT_KEY = 'my:component';
|
||||
const DUMMY_INSTANCE_KEY = 'myInstance';
|
||||
it('returns ok', async () => {
|
||||
const measures = {
|
||||
analysisDate: '2022-01-01T00:00:00Z',
|
||||
measures: [{ metric: 'vulnerabilities', value: '54' }],
|
||||
};
|
||||
|
||||
getFindingsMock.mockReturnValue(Promise.resolve(measures));
|
||||
const response = await request(app)
|
||||
.get('/findings')
|
||||
.set('componentKey', 'my:app')
|
||||
.query({
|
||||
componentKey: DUMMY_COMPONENT_KEY,
|
||||
instanceKey: DUMMY_INSTANCE_KEY,
|
||||
})
|
||||
.send();
|
||||
expect(getFindingsMock).toBeCalledTimes(1);
|
||||
expect(getFindingsMock).toBeCalledWith(
|
||||
DUMMY_COMPONENT_KEY,
|
||||
DUMMY_INSTANCE_KEY,
|
||||
);
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual(measures);
|
||||
});
|
||||
it('returns an error when component key is not defined', async () => {
|
||||
const response = await request(app)
|
||||
.get('/findings')
|
||||
.query({
|
||||
instanceKey: DUMMY_INSTANCE_KEY,
|
||||
})
|
||||
.send();
|
||||
|
||||
expect(response.status).toEqual(400);
|
||||
});
|
||||
|
||||
it('use an empty string as instance name when instance key not provided', async () => {
|
||||
const measures = {
|
||||
analysisDate: '2021-04-08',
|
||||
measures: [{ metric: 'vulnerabilities', value: '54' }],
|
||||
};
|
||||
|
||||
getFindingsMock.mockReturnValue(Promise.resolve(measures));
|
||||
const response = await request(app)
|
||||
.get('/findings')
|
||||
.query({
|
||||
componentKey: DUMMY_COMPONENT_KEY,
|
||||
})
|
||||
.send();
|
||||
|
||||
expect(getFindingsMock).toBeCalledTimes(1);
|
||||
expect(getFindingsMock).toBeCalledWith(DUMMY_COMPONENT_KEY, '');
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({
|
||||
analysisDate: '2022-10-22T04:55:23Z',
|
||||
measures: [{ metric: 'coverage', value: '50' }],
|
||||
});
|
||||
expect(response.body).toEqual(measures);
|
||||
});
|
||||
});
|
||||
describe('GET /instanceUrl', () => {
|
||||
const DUMMY_INSTANCE_KEY = 'myInstance';
|
||||
const DUMMY_INSTANCE_URL = 'http://sonarqube.example.com';
|
||||
it('returns ok', async () => {
|
||||
getBaseUrlMock.mockReturnValue(DUMMY_INSTANCE_URL);
|
||||
const response = await request(app)
|
||||
.get('/instanceUrl')
|
||||
.query({
|
||||
instanceKey: DUMMY_INSTANCE_KEY,
|
||||
})
|
||||
.send();
|
||||
expect(getBaseUrlMock).toBeCalledTimes(1);
|
||||
expect(getBaseUrlMock).toBeCalledWith(DUMMY_INSTANCE_KEY);
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({ instanceUrl: DUMMY_INSTANCE_URL });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,37 +15,87 @@
|
||||
*/
|
||||
|
||||
import { errorHandler } from '@backstage/backend-common';
|
||||
import express from 'express';
|
||||
import express, { RequestHandler } from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import { Logger } from 'winston';
|
||||
import {
|
||||
SonarqubeFindings,
|
||||
SonarqubeInfoProvider,
|
||||
} from './sonarqubeInfoProvider';
|
||||
import { InputError } from '../../../../packages/errors';
|
||||
|
||||
/**
|
||||
* Dependencies needed by the router
|
||||
* @public
|
||||
*/
|
||||
export interface RouterOptions {
|
||||
/**
|
||||
* Logger for logging purposes
|
||||
*/
|
||||
logger: Logger;
|
||||
/**
|
||||
* Info provider to be able to get all necessary information for the APIs
|
||||
*/
|
||||
sonarqubeInfoProvider: SonarqubeInfoProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* Constructs a sonarqube router.
|
||||
*
|
||||
* Expose endpoint to get information on or for a sonarqube instance.
|
||||
*
|
||||
* @param options - Dependencies of the router
|
||||
*/
|
||||
export async function createRouter(
|
||||
options: RouterOptions,
|
||||
): Promise<express.Router> {
|
||||
const { logger } = options;
|
||||
const { logger, sonarqubeInfoProvider } = options;
|
||||
|
||||
const router = Router();
|
||||
router.use(express.json());
|
||||
// mock api for now
|
||||
router.get('/findings', (request, response) => {
|
||||
logger.info(request.params);
|
||||
router.get('/findings', (async (request, response) => {
|
||||
const componentKey = request.query.componentKey;
|
||||
let instanceKey = request.query.instanceKey;
|
||||
|
||||
if (!componentKey)
|
||||
throw new InputError('ComponentKey must be provided as a single string.');
|
||||
|
||||
if (!instanceKey) {
|
||||
instanceKey = '';
|
||||
logger.info(
|
||||
`Retrieving findings for component ${componentKey} in default sonarqube instance`,
|
||||
);
|
||||
} else {
|
||||
logger.info(
|
||||
`Retrieving findings for component ${componentKey} in sonarqube instance name ${instanceKey}`,
|
||||
);
|
||||
}
|
||||
|
||||
response.send(
|
||||
await sonarqubeInfoProvider.getFindings(componentKey, instanceKey),
|
||||
);
|
||||
}) as RequestHandler<unknown, SonarqubeFindings | undefined, unknown, { componentKey: string; instanceKey: string }>);
|
||||
|
||||
router.get('/instanceUrl', ((request, response) => {
|
||||
let requestedInstanceKey = request.query.instanceKey;
|
||||
if (requestedInstanceKey) {
|
||||
logger.info(
|
||||
`Retrieving sonarqube instance URL for key ${requestedInstanceKey}`,
|
||||
);
|
||||
} else {
|
||||
requestedInstanceKey = '';
|
||||
logger.info(
|
||||
`Retrieving default sonarqube instance URL as parameter is inexistant, empty or malformed`,
|
||||
);
|
||||
}
|
||||
response.send({
|
||||
analysisDate: '2022-10-22T04:55:23Z',
|
||||
measures: [{ metric: 'coverage', value: '50' }],
|
||||
instanceUrl: sonarqubeInfoProvider.getBaseUrl(requestedInstanceKey),
|
||||
});
|
||||
});
|
||||
router.get('/instanceUrl', (request, response) => {
|
||||
logger.info(request.params);
|
||||
response.send({
|
||||
instanceUrl: `https://instance.local?${encodeURI(
|
||||
request.query.instanceKey as string,
|
||||
)}`,
|
||||
});
|
||||
});
|
||||
}) as RequestHandler<unknown, { instanceUrl: string }, unknown, { instanceKey: string }>);
|
||||
|
||||
router.use(errorHandler());
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -14,10 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createServiceBuilder } from '@backstage/backend-common';
|
||||
import {
|
||||
createServiceBuilder,
|
||||
loadBackendConfig,
|
||||
} from '@backstage/backend-common';
|
||||
import { Server } from 'http';
|
||||
import { Logger } from 'winston';
|
||||
import { createRouter } from './router';
|
||||
import { DefaultSonarqubeInfoProvider } from './sonarqubeInfoProvider';
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
@@ -30,13 +34,15 @@ export async function startStandaloneServer(
|
||||
): Promise<Server> {
|
||||
const logger = options.logger.child({ service: 'sonarqube-backend-backend' });
|
||||
logger.debug('Starting application server...');
|
||||
const config = await loadBackendConfig({ logger, argv: process.argv });
|
||||
const router = await createRouter({
|
||||
logger,
|
||||
sonarqubeInfoProvider: DefaultSonarqubeInfoProvider.fromConfig(config),
|
||||
});
|
||||
|
||||
let service = createServiceBuilder(module)
|
||||
.setPort(options.port)
|
||||
.addRouter('/sonarqube-backend', router);
|
||||
.addRouter('/sonarqube', router);
|
||||
if (options.enableCors) {
|
||||
service = service.enableCors({ origin: 'http://localhost:3000' });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user