dev: add get() and post() to extension API

This commit is contained in:
KernelDeimos 2024-10-31 21:08:38 -04:00
parent d1ebbbe3c7
commit 3f6900f26b
5 changed files with 137 additions and 0 deletions

View File

@ -0,0 +1,7 @@
extension.get('/example-mod-get', (req, res) => {
res.send('Hello World!');
});
extension.on('install', ({ services }) => {
console.log('install was called');
})

View File

@ -0,0 +1,12 @@
{
"name": "example-puter-extension",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "AGPL-3.0-only"
}

View File

@ -1,6 +1,7 @@
const { AdvancedBase } = require("@heyputer/putility");
const EmitterFeature = require("@heyputer/putility/src/features/EmitterFeature");
const { Context } = require("./util/context");
const { ExtensionService, ExtensionServiceState } = require("./ExtensionService");
class Extension extends AdvancedBase {
static FEATURES = [
@ -12,6 +13,51 @@ class Extension extends AdvancedBase {
]
}),
];
constructor (...a) {
super(...a);
this.service = null;
}
get (path, handler, options) {
// this extension will have a default service
this.ensure_service_();
// handler and options may be flipped
if ( typeof handler === 'object' ) {
[handler, options] = [options, handler];
}
if ( ! options ) options = {};
this.service.register_route_handler_(path, handler, {
...options,
methods: ['GET'],
});
}
post (path, handler, options) {
// this extension will have a default service
this.ensure_service_();
// handler and options may be flipped
if ( typeof handler === 'object' ) {
[handler, options] = [options, handler];
}
if ( ! options ) options = {};
this.service.register_route_handler_(path, handler, {
...options,
methods: ['POST'],
});
}
ensure_service_ () {
if ( this.service ) {
return;
}
this.service = new ExtensionServiceState();
}
}
module.exports = {

View File

@ -1,10 +1,18 @@
const { AdvancedBase } = require("@heyputer/putility");
const uuid = require('uuid');
const { ExtensionService } = require("./ExtensionService");
class ExtensionModule extends AdvancedBase {
async install (context) {
const services = context.get('services');
this.extension.emit('install', { context, services })
if ( this.extension.service ) {
services.registerService(uuid.v4(), ExtensionService, {
state: this.extension.service,
}); // uuid for now
}
}
}

View File

@ -0,0 +1,64 @@
const { AdvancedBase } = require("@heyputer/putility");
const BaseService = require("./services/BaseService");
const { Endpoint } = require("./util/expressutil");
class ExtensionServiceState extends AdvancedBase {
constructor (...a) {
super(...a);
this.endpoints_ = [];
}
register_route_handler_ (path, handler, options = {}) {
// handler and options may be flipped
if ( typeof handler === 'object' ) {
[handler, options] = [options, handler];
}
const mw = options.mw ?? [];
// TODO: option for auth middleware is harcoded here, but eventually
// all exposed middlewares should be registered under the simpele names
// used in this options object (probably; still not 100% decided on that)
if ( options.auth ) {
const auth_conf = typeof options.auth === 'object' ?
options.auth : {};
mw.push(configurable_auth(auth_conf));
}
const endpoint = Endpoint({
methods: options.methods ?? ['GET'],
mw,
route: path,
handler: handler,
});
this.endpoints_.push(endpoint);
}
}
/**
* A service that does absolutely nothing by default, but its behavior can be
* extended by adding route handlers and event listeners. This is used to
* provide a default service for extensions.
*/
class ExtensionService extends BaseService {
_construct () {
this.extension = null;
this.endpoints_ = [];
}
async _init (args) {
this.state = args.state;
}
['__on_install.routes'] (_, { app }) {
for ( const endpoint of this.state.endpoints_ ) {
endpoint.attach(app);
}
}
}
module.exports = {
ExtensionService,
ExtensionServiceState,
};