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

Draft: Start of simple plugin system #2

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
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
12 changes: 11 additions & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,14 @@ jobs:
- run: ./run-test.sh
working-directory: ./tests/e2e

- run: curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"path":"/hi"}'
- run: >
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"path":"/index.php"}' | grep -q '{"statusCode":200,"headers"'

- run: >
curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"path":"/index.php", "postRequestPlugin": "1"}' | grep -q '{"statusCode":201,"body":"Foo"}'

- run: >
curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"path":"/index.php", "preRequestPlugin": "1"}' | grep -q '{"statusCode":200,"body":"Foo"}'

- run: >
curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"path":"/static.css"}' | grep -q 'background-color: blue'
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"jest": "^29.5.0"
},
"scripts": {
"test": "jest --testPathIgnorePatterns 'e2e'"
"test": "jest --testPathIgnorePatterns 'e2e'",
"local-tests": "npm run test && cd tests/e2e && ./local.sh"
}
}
16 changes: 11 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@ const phpPath = path.resolve(__dirname, '../php-files/php');
const phpIniPath = path.resolve(__dirname, '../php-files/php.ini');
const cwd = path.resolve(__dirname, '../php-files');

const plugins = require('./plugins');

async function handler(data) {
await validate(data);

const { event, docRoot } = data;

const preRequestResponse = await plugins.executePreRequest(event);
if (preRequestResponse != null) {
return preRequestResponse;
}

if (!php) {
const env = {
Expand Down Expand Up @@ -98,7 +105,6 @@ async function handler(data) {
urlPath = event.rawPath;
}


let requestHeaders;
if (event.cookies) {
let cookielist = '';
Expand Down Expand Up @@ -176,7 +182,6 @@ async function handler(data) {
}
}


if (!headers['cache-control'] && response.status === 200 && (!data.hasOwnProperty('skipCacheControl') || (data.hasOwnProperty('skipCacheControl') && !data.skipCacheControl))) {
let cacheControl = 'max-age=3600, s-maxage=86400';

Expand Down Expand Up @@ -208,7 +213,7 @@ async function handler(data) {
returnResponse.multiValueHeaders = multiHeaders;
}

return returnResponse;
return await plugins.executePostRequest(event, returnResponse);
}
catch (err) {
console.log(err);
Expand Down Expand Up @@ -237,7 +242,6 @@ async function validate(data) {
throw new Error("The event property cannot be empty.");
}


if (!data.hasOwnProperty("docRoot")) {
throw new Error("The docRoot or routerScript property is required.");
}
Expand Down Expand Up @@ -265,4 +269,6 @@ async function exists(path) {
}

module.exports = handler;
module.exports.validate = validate;
module.exports.validate = validate;
module.exports.registerPlugin = plugins.register;
module.exports.getPlugins = plugins.getPlugins;
56 changes: 56 additions & 0 deletions src/plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
let plugins = [];

function register(plugin) {
// Require a plugin name.
if (!plugin.hasOwnProperty("name") || !plugin.name) {
throw new Error("Plugins are required to have a name");
}

// Don't allow double registration.
if (plugins.find((plug) => plug.name === plugin.name)) {
throw new Error("Plugins can only be registered once");
}

plugins.push(plugin);
}

function getPlugins() {
return plugins;
}

async function executePreRequest(event) {
let response = null;
//@TODO: only loop through plugins with preRequest.
for (let i = 0; i < plugins.length; i++) {
let plugin = plugins[i];
if (plugin.preRequest) {
response = await plugin.preRequest(event, response);
}
}

//@TODO: validate response
return response;
}

async function executePostRequest(event, response) {
//@TODO: only loop through plugins with postRequest.
for (let i = 0; i < plugins.length; i++) {
let plugin = plugins[i];
let pluginResponse;

if (plugin.postRequest) {
pluginResponse = await plugin.postRequest(event, response);
if (pluginResponse) {
response = pluginResponse;
}
}
}

//@TODO: validate response
return response;
}

module.exports.register = register;
module.exports.getPlugins = getPlugins;
module.exports.executePreRequest = executePreRequest;
module.exports.executePostRequest = executePostRequest;
8 changes: 6 additions & 2 deletions tests/e2e/local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@

docker run -p 9000:8080 -d --name serverlesswp-local docker-lambda-serverlesswp

curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"path":"/index.php"}'
curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"path":"/index.php"}' | grep -q '{"statusCode":200,"headers"' || echo 'fail 1'

curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"path":"/static.css"}'
curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"path":"/index.php", "postRequestPlugin": "1"}' | grep -q '{"statusCode":201,"body":"Foo"}' || echo 'fail 2'

curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"path":"/index.php", "preRequestPlugin": "1"}' | grep -q '{"statusCode":200,"body":"Foo"}' || echo 'fail 3'

curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"path":"/static.css"}' | grep -q 'background-color: blue' || echo 'fail 4'

docker stop serverlesswp-local
docker rm serverlesswp-local
26 changes: 26 additions & 0 deletions tests/e2e/test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
const serverlesswp = require('./src/index');
const path = require('path');

serverlesswp.registerPlugin({
name: 'postRequest',
postRequest: async function(event, pluginResponse) {
// Bypass this plugin conditionally
if (event.hasOwnProperty("postRequestPlugin")) {
return {
statusCode: pluginResponse.statusCode + 1,
body: 'Foo'
}
}
}
});

serverlesswp.registerPlugin({
name: 'preRequest',
preRequest: async function(event) {
// Bypass this plugin conditionally
if (event.hasOwnProperty("preRequestPlugin")) {
return {
statusCode: 200,
body: 'Foo'
}
}
}
});

exports.handler = async function (event, context, callback) {
const docRoot = path.join(process.cwd(), 'wp');
const routerScript = path.join(process.cwd(), 'router.php');
Expand Down
9 changes: 8 additions & 1 deletion tests/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ test('Error with missing event property', () => {
expect(async () => await serverlesswp.validate(args)).rejects.toThrow();
});


test('Error with empty event property', () => {
const args = {event: '', docRoot: '/'}
expect(async () => await serverlesswp.validate(args)).rejects.toThrow();
Expand All @@ -24,4 +23,12 @@ test('Error with invalid docRoot property', () => {
test('Error with invalid routerScript property', () => {
const args = {event: {}, docRoot: '/', routerScript: '/invalid'}
expect(async () => await serverlesswp.validate(args)).rejects.toThrow();
});

test('Plugin registration', () => {
serverlesswp.registerPlugin({name: 'foo'});
serverlesswp.registerPlugin({name: 'bar'});

const plugins = serverlesswp.getPlugins();
expect(plugins).toHaveLength(2);
});
89 changes: 89 additions & 0 deletions tests/plugins.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
const plugins = require('../src/plugins');

test('Plugin registration', () => {
plugins.register({name: 'foo'});
plugins.register({name: 'bar'});

const pluginList = plugins.getPlugins();
expect(pluginList).toHaveLength(2);
});

test('Plugins need a name', () => {
expect(() => { plugins.register({}) }).toThrow();
expect(() => { plugins.register({name: ''}) }).toThrow();
});

test('Plugins can only be registered once', () => {
plugins.register({name: 'baz'});
expect(() => { plugins.register({name: 'baz'}) }).toThrow();
});

test('Do not execute non-existent preRequest', async () => {
plugins.register({name: 'Test'});

const response = await plugins.executePreRequest({test: 'test'});
expect(response).toBeNull();
});

test('Execute preRequest', async () => {
const statusCode = 200;
const body = 'foo';

plugins.register({
name: 'preRequest',
preRequest: async function(event, response) {
return {
statusCode: 200,
body: event.test,
}
},
});

const response = await plugins.executePreRequest({test: body});
expect(response.body).toEqual(body);
expect(response.statusCode).toEqual(statusCode);
});

test('Execute multiple preRequest', async () => {
const body = 'foo';

plugins.register({
name: '1',
preRequest: async function(event, response) {
return {
statusCode: 200,
body: event.test,
}
},
});

plugins.register({
name: '2',
preRequest: async function(event, response) {
if (response?.statusCode) {
response.statusCode = response.statusCode + 1;
}
return response;
},
});

const response = await plugins.executePreRequest({test: body});
expect(response.body).toEqual(body);
expect(response.statusCode).toEqual(201);
});

test('Execute postRequest', async () => {
plugins.register({
name: 'postRequest',
postRequest: async function(event, response) {
return {
statusCode: response.statusCode + 1,
body: 'Foo'
}
}
});

const response = await plugins.executePostRequest({test: 'foo'}, {statusCode: 200, body: 'Test'});
expect(response.body).toEqual('Foo');
expect(response.statusCode).toEqual(201);
});
Loading