Skip to content

Commit

Permalink
Load plugin list from files/plugins.conf
Browse files Browse the repository at this point in the history
  • Loading branch information
darashi committed Mar 6, 2024
1 parent 599f231 commit 6598a7b
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 45 deletions.
11 changes: 5 additions & 6 deletions PLUGINS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ Typical use cases include, but are not limited to, rewriting prefixes to improve

## Usage

List the directories of plugins to be used in `plugin.conf`. If the line starts with `#`, it is treated as a comment and ignored.

Then specify the path to `plugin.conf` to `PLUGINS` environment variable when starting the SPARQL-proxy. For example, `PLUGINS=/path/to/plugin.conf`. This will activate the plugin mechanism.
List the directories of plugins to be used in `files/plugin.conf`. If the line starts with `#`, it is treated as a comment and ignored.

Note that plugins are applied in the order specified in this file. That is, the first plugin in the list receives from the request from the client, then next plugin receives the request from the previous plugin, and so on. Responses are processed in the reverse order.

Expand Down Expand Up @@ -40,17 +38,18 @@ plugins
└── main.ts
```

Create `plugins.conf` which contains the path to the `noop` plugin:
Create `files/plugins.conf` which contains the path to the `noop` plugin:

```
# files/plugins.conf
./plugins/noop
```

Then start the SPARQL-proxy with `PLUGINS` environment variable:
Then start SPARQL-proxy:


```
PLUGINS=./plugins.conf SPARQL_BACKEND=https://example.com/sparql npm start
❯ SPARQL_BACKEND=https://example.com/sparql npm start
> [email protected] start
> tsx src/server.mjs
Expand Down
19 changes: 9 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,16 +214,6 @@ Set `true` to enable passthrough mode. If enabled, queries are sent to the backe

You should enable this feature only when you understand exactly what you are doing.

### `PLUGINS`

THIS IS AN EXPERIMENTAL FEATURE.

(default: none)

Set the path to the plugins configuration file. List the paths of the plugins to be used in this file in the order you want to apply them.

This feature does not work with the query splitting mode and the passthrough mode.

## Serving SPARQL Service Description

If you want to serve SPARQL service description, put the descriptions under `files` directory with the name `description.[format]`.
Expand All @@ -235,6 +225,15 @@ NOTE: If you're running `sparql-proxy` within Docker, you may want to use `-v` o
$ docker run -p 8080:3000 -e SPARQL_BACKEND=http://example.com/sparql -v `pwd`/files:/app/files dbcls/sparql-proxy


## Plugins

THIS IS AN EXPERIMENTAL FEATURE.

SPARQL-proxy has a plugin system to extend its functionalities. In order to activate this feature, create `plugins.conf` file in `files` directory. The paths of the plugins to be used should be listed in this file, in the order you want to apply them. See more details in [PLUGINS.md](PLUGINS.md).

This feature does not work with the query splitting mode and the passthrough mode.


## Relaying `X-SPARQL-` headers

sparql-proxy relays HTTP headers starting with `X-SPARQL-` received from backends. This is intended to pass through `X-SPARQL-MaxRows`, which is emitted from Virtuoso.
Expand Down
63 changes: 40 additions & 23 deletions src/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,37 +83,54 @@ type Plugin = {

type PluginFunc = (ctx: Context, next: PluginFunc) => Promise<Response>;

export default class Plugins {
pluginsConfPath: string;
plugins: Plugin[] = [];
async function loadPluginConf(
pluginsConfPath: string,
): Promise<string[] | undefined> {
let text: string | undefined = undefined;
try {
text = await fs.readFile(pluginsConfPath, "utf-8");
} catch (e) {
console.log(
`plugin: plugin configuration ${pluginsConfPath} is not available`,
);
return undefined;
}

constructor(pluginsConfPath: string) {
this.pluginsConfPath = pluginsConfPath;
const paths: string[] = [];
for (const line of text.split(/\r?\n/)) {
if (line === "") continue;
if (line.startsWith("#")) continue;

paths.push(line);
}
return paths;
}

async loadConfig(): Promise<string[]> {
const text = await fs.readFile(this.pluginsConfPath, "utf-8");
const paths: string[] = [];
for (const line of text.split(/\r?\n/)) {
if (line === "") continue;
if (line.startsWith("#")) continue;
export default class Plugins {
plugins: Plugin[] = [];

paths.push(line);
static async load(pluginsConfPath: string): Promise<Plugins | undefined> {
const plugins = new Plugins();
const pluginPaths = await loadPluginConf(pluginsConfPath);
if (!pluginPaths) {
return undefined;
}
return paths;
}

async load() {
const pluginPaths = await this.loadConfig();
const plugins: Plugin[] = [];
for (const dir of pluginPaths) {
const resolvedPath = path.resolve(dir);
console.log(`plugin: loading ${resolvedPath}`);
const plugin = await import(`${resolvedPath}/main`);
plugins.push(plugin);
console.log(`plugin: loaded ${resolvedPath}`);
const plugin = await plugins.importPlugin(dir);
plugins.plugins.push(plugin);
}
this.plugins = plugins;

return plugins;
}

async importPlugin(pluginPath: string): Promise<Plugin> {
const resolvedPath = path.resolve(pluginPath);
console.log(`plugin: loading ${resolvedPath}`);
const plugin = import(`${resolvedPath}/main`);
console.log(`plugin: loaded ${resolvedPath}`);

return plugin;
}

async apply(
Expand Down
8 changes: 2 additions & 6 deletions src/server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ const fs = _fs.promises;
port: Number(process.env.PORT || 3000),
queryLogPath: process.env.QUERY_LOG_PATH,
trustProxy: process.env.TRUST_PROXY || "false",
pluginsConfPath: process.env.PLUGINS,
});

const secret = `${config.adminUser}:${config.adminPassword}`;
Expand Down Expand Up @@ -81,11 +80,8 @@ const fs = _fs.promises;
process.env,
);

let plugins = null;
if (config.pluginsConfPath) {
plugins = new Plugins(config.pluginsConfPath);
await plugins.load();
} else {
const plugins = await Plugins.load("./files/plugins.conf");
if (!plugins) {
console.log("plugin: disabled");
}

Expand Down

0 comments on commit 6598a7b

Please sign in to comment.