Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement library for custom scaffolder actions. #59

Merged
merged 4 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions config/accept.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"method": "GET",
"path": "/agent-provider/*",
"origin": "http://localhost:7044"
} ,
{
"method": "GET",
"path": "/scaffolder-action/*",
"origin": "http://localhost:7044"
}
],
"public": [
Expand Down
15 changes: 12 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"keywords": [],
"version": "1.0.0",
"engines": {
"node": ">=16"
"node": ">=18"
},
"publishConfig": {
"access": "public",
Expand All @@ -36,27 +36,35 @@
},
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/client-s3": "^3.515.0",
"@aws-sdk/lib-storage": "^3.515.0",
"@aws-sdk/s3-request-presigner": "^3.515.0",
"@backstage/catalog-model": "^1.4.1",
"@backstage/plugin-catalog-backend": "^1.11.0",
"@backstage/plugin-catalog-node": "^1.3.6",
"@changesets/cli": "^2.26.2",
"archiver": "^6.0.1",
"express": "^4.17.1",
"node-fetch": "^2.6.11",
"pino": "^8.14.2",
"snyk-broker": "^4.161.5"
"snyk-broker": "^4.161.5",
"unzipper": "^0.10.14"
},
"devDependencies": {
"@types/archiver": "^6.0.2",
"@types/chai": "^4.2.18",
"@types/fs-extra": "^9.0.11",
"@types/mocha": "^8.2.2",
"@types/node": "^15.6.0",
"@types/node": "^18.15.3",
"@types/node-fetch": "^2.6.2",
"@types/sinon": "^10.0.13",
"@types/unzipper": "^0.10.9",
"@typescript-eslint/eslint-plugin": "^4.25.0",
"@typescript-eslint/parser": "^4.25.0",
"chai": "^4.3.4",
"conventional-changelog-cli": "^2.1.1",
"copyfiles": "^2.4.1",
"esbuild": "^0.20.0",
"eslint": "^7.27.0",
"eslint-plugin-prettier": "^3.4.0",
"fs-extra": "^10.0.0",
Expand All @@ -68,6 +76,7 @@
"source-map-support": "^0.5.19",
"ts-mock-imports": "^1.3.8",
"tsc-alias": "^1.2.11",
"tsx": "^4.7.1",
"typescript": "next"
}
}
13 changes: 7 additions & 6 deletions src/lib/RoadieAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import {
TechInsightsDataSourceAgentConfiguration,
} from '$/types';
import { RoadieAgentReceiver } from '@/RoadieAgentReceiver';
import { RoadieAgentForwarder } from '@/RoadieAgentForwarder';

const BROKER_CLIENT_URL = 'http://localhost';

export class RoadieAgent {
constructor(private readonly config: RoadieAgentConfiguration) {}
constructor(private readonly config: RoadieAgentConfiguration) {
}

private agentConfigurations: AvailableAgentConfiguration[] = [];

static fromConfig(
config: RoadieAgentConfiguration = {
server: 'http://localhost:7341',
Expand All @@ -32,12 +34,14 @@ export class RoadieAgent {
this.agentConfigurations.push(entityProviderAgentConfiguration);
return this;
}

addScaffolderAction(
scaffolderActionAgentConfiguration: ScaffolderActionAgentConfiguration,
) {
this.agentConfigurations.push(scaffolderActionAgentConfiguration);
return this;
}

addTechInsightsDataSource(
techInsightsDataSourceConfiguration: TechInsightsDataSourceAgentConfiguration,
) {
Expand All @@ -46,12 +50,9 @@ export class RoadieAgent {
}

start() {
const forwarder = new RoadieAgentForwarder({
brokerClientUrl: `${BROKER_CLIENT_URL}:${this.config.port}`,
});
const receiver = new RoadieAgentReceiver(
this.agentConfigurations,
forwarder,
`${BROKER_CLIENT_URL}:${this.config.port}`,
{
port: this.config.agentPort,
},
Expand Down
43 changes: 0 additions & 43 deletions src/lib/RoadieAgentForwarder.ts

This file was deleted.

63 changes: 52 additions & 11 deletions src/lib/RoadieAgentReceiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,29 @@ import {
AvailableAgentConfiguration,
EntityProviderAgentConfiguration,
HandlerConfig,
ScaffolderActionAgentConfiguration,
} from '$/types';
import { RoadieAgentForwarder } from '@/RoadieAgentForwarder';
import { getLogger } from '@/logger';
import { BaseLogger } from 'pino';
import { CustomScaffolderAction } from '@/scaffolderAction/CustomScaffolderAction';
import { createEntityEmitter } from '@/entityProvider/createEntityEmitter';

export class RoadieAgentReceiver {
private server: Express;
private agentConfigurations: Map<string, AvailableAgentConfiguration>;
private handlerConfig: HandlerConfig;
private forwarder: RoadieAgentForwarder;
private logger: BaseLogger;
private readonly agentConfigurations: Map<string, AvailableAgentConfiguration>;
private readonly handlerConfig: HandlerConfig;
private readonly logger: BaseLogger;
private readonly brokerClientUrl: string;

constructor(
agentConfigurations: AvailableAgentConfiguration[],
forwarder: RoadieAgentForwarder,
brokerClientUrl: string,
handlerConfig: HandlerConfig,
) {
this.server = this.constructRequestListener(agentConfigurations);
this.handlerConfig = handlerConfig;
this.forwarder = forwarder;
this.logger = getLogger('RoadieAgentForwarder');
this.brokerClientUrl = brokerClientUrl;
this.logger = getLogger('RoadieAgentReceiver');
this.agentConfigurations = agentConfigurations.reduce(
(acc, agentConfiguration) => {
acc.set(agentConfiguration.name, agentConfiguration);
Expand All @@ -32,6 +34,7 @@ export class RoadieAgentReceiver {
new Map<string, AvailableAgentConfiguration>(),
);
}

start() {
this.logger.info('Starting Roadie Agent Receiver webserver');
this.server.listen(this.handlerConfig.port);
Expand All @@ -47,8 +50,12 @@ export class RoadieAgentReceiver {
this.registerEntityProvider(configuration, app);

break;
case 'tech-insights-data-source':
case 'scaffolder-action':
this.registerScaffolderAction(configuration, app);

break;
case 'tech-insights-data-source':

throw new Error(
`Roadie Agent functionality of type ${configuration.type} not yet implemented.`,
);
Expand All @@ -64,8 +71,8 @@ export class RoadieAgentReceiver {
) {
// Specifying routes explicitly
app.get(`/agent-provider/${configuration.name}`, (req, res) => {
const entityEmitter = this.forwarder.createEntityEmitter(
configuration.name,
const entityEmitter = createEntityEmitter(
configuration.name, this.brokerClientUrl,
);
this.logger.info(
`Received entity emitting trigger for endpoint ${configuration.name}`,
Expand All @@ -76,4 +83,38 @@ export class RoadieAgentReceiver {
);
});
}

private registerScaffolderAction(
configuration: ScaffolderActionAgentConfiguration,
app: Express,
) {
// Specifying routes explicitly
app.get(`/scaffolder-action/${configuration.name}`, (req, res) => {
Xantier marked this conversation as resolved.
Show resolved Hide resolved
this.logger.info(
`Received scaffolder action trigger for endpoint ${configuration.name}`,
);

const body = JSON.parse(Buffer.from(req.query.body as string, 'base64').toString());
const workspaceUrl = req.query.presign !== 'undefined' ? Buffer.from(req.query.presign as string, 'base64').toString() : undefined;
const actionId = req.query.actionId as string;

const scaffolderAction = new CustomScaffolderAction({
configuration,
actionId,
brokerClientUrl: this.brokerClientUrl,
payload: {
body, workspaceUrl,
},
});
void scaffolderAction.start();

res.json({
message:
`Triggered custom scaffolder action event for Roadie Agent ${configuration.name}`,
});
})
;
}


}
1 change: 1 addition & 0 deletions src/lib/entityProvider/constants.ts
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entityProvider side of the agent is untouched, just moved to a folder

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const AGENT_ENTITY_PROVIDER_PATH = `api/catalog/roadie-agent/`;
36 changes: 36 additions & 0 deletions src/lib/entityProvider/createEntityEmitter.ts
Xantier marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { EntityProviderMutation } from '@backstage/plugin-catalog-node';
import { AGENT_ENTITY_PROVIDER_PATH } from '@/entityProvider/constants';
import fetch from 'node-fetch';
import { getLogger } from '@/logger';

export const createEntityEmitter = (target: string, brokerClientUrl: string) => {

const logger = getLogger('RoadieAgentEntityEmitter');

logger.info(`Creating new entity emitter for target ${target}`);
return async (mutation: EntityProviderMutation) => {
logger.info(`Forwarding mutation to target ${target}`);
const url = `${brokerClientUrl}/${AGENT_ENTITY_PROVIDER_PATH}`;
const body = JSON.stringify({
target,
payload: mutation,
});
const response = await fetch(url, {
method: 'POST',
body,
headers: {
'Content-Type': 'application/json',
},
});
try {
const responseBody = await response.text();
logger.info(
`Received response from forwarder mutation. Status: ${response.status}, ${response.statusText}. Body: ${responseBody}`,
);
} catch (e) {
logger.warn(
`No Response received from forwarder mutation. Status: ${response?.status}, ${response?.statusText}.`,
);
}
};
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { RoadieAgentForwarder } from '@/RoadieAgentForwarder';
import { EntityProviderMutation } from '@backstage/plugin-catalog-node';
import { Entity } from '@backstage/catalog-model';
import * as nodeFetchModule from 'node-fetch';
import sinon from 'sinon';
import { createEntityEmitter } from '@/entityProvider/createEntityEmitter';

const entity: Entity = {
metadata: {
Expand Down Expand Up @@ -34,15 +34,13 @@ const fakePayload: EntityProviderMutation = {
],
};

describe('RoadieAgentForwarder', () => {
describe('EntityProvider.entityEmitter', () => {
it('should emit entity when triggered', async () => {
const fetchStub = sinon.stub(nodeFetchModule, 'default');
const connectionToken = 'my-target-broker-connection';
const brokerClientUrl = 'http://local.host';

const forwarder = new RoadieAgentForwarder({
brokerClientUrl: 'http://local.host',
});
const emitter = forwarder.createEntityEmitter(connectionToken);
const emitter = createEntityEmitter(connectionToken, brokerClientUrl);
await emitter(fakePayload);
sinon.assert.calledOnce(fetchStub);
sinon.assert.calledWith(
Expand Down
3 changes: 2 additions & 1 deletion src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { RoadieAgent } from './RoadieAgent';
export { createRoadieAgentEntityProvider } from './entityProviderAgent';
export { createRoadieAgentEntityProvider } from './entityProvider/entityProviderAgent';
export { createRoadieAgentScaffolderAction } from './scaffolderAction/scaffolderActionAgent';
Loading
Loading