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

Collection paging #208

Open
wants to merge 18 commits into
base: hydra-collections
Choose a base branch
from
Open
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
5 changes: 5 additions & 0 deletions .changeset/short-lemons-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kopflos-cms/core": minor
---

Changed plugin setup to require classes
5 changes: 5 additions & 0 deletions .changeset/shy-bobcats-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kopflos-cms/core": patch
---

Added helper to easily access plugin instance
5 changes: 5 additions & 0 deletions .changeset/tall-maps-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kopflos-cms/hydra": minor
---

Created extensible method for collection paging strategies
7 changes: 7 additions & 0 deletions .changeset/tidy-mice-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@kopflos-cms/express": patch
"@kopflos-cms/vite": patch
"kopflos": patch
---

Plugins are now implemented as classes
19 changes: 19 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,22 @@ jobs:
node-version: lts/*
- run: npm ci
- run: npm run -ws --if-present build

smoke-test-example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: lando/setup-lando@v3
with:
lando-version: 3.21.2
- uses: tpluscode/[email protected]
with:
healthcheck: https://read-the-plaque.lndo.site/plaque/newton-s-apple-tree-monash-university
timeout: 60000
ignore-errors: true

- run: curl https://read-the-plaque.lndo.site/plaque/newton-s-apple-tree-monash-university.html -Ifk

- run: lando logs
if: failure()
34 changes: 34 additions & 0 deletions .lando.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: read-the-plaque
services:
oxigraph:
api: 3
type: lando
entrypoint: /usr/local/bin/oxigraph
app_mount: false
scanner: false
ssl: true
services:
image: ghcr.io/oxigraph/oxigraph:0.4.6
user: root
command: serve --location /data --bind 0.0.0.0:7878
ports:
- 7878
volumes:
- ./example/oxigraph:/data
app:
type: node:20
ssl: true
scanner: false
build:
- npm i
command: cd example; kopflos serve --mode development --trust-proxy
overrides:
environment:
API_BASE: https://read-the-plaque.lndo.site
DB_URI: http://db.read-the-plaque.lndo.site

proxy:
oxigraph:
- db.read-the-plaque.lndo.site:7878
app:
- read-the-plaque.lndo.site:1429
2 changes: 1 addition & 1 deletion example/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
oxigraph:
image: ghcr.io/oxigraph/oxigraph:0.4.4
image: ghcr.io/oxigraph/oxigraph:0.4.6
user: root
command: serve --location /data --bind 0.0.0.0:7878
ports:
Expand Down
15 changes: 9 additions & 6 deletions example/kopflos.config.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import * as url from 'node:url'
import type { KopflosConfig } from '@kopflos-cms/core'

const baseIri = process.env.API_BASE || 'http://localhost:1429'
const dbUri = process.env.DB_URI || 'http://localhost:7878'

export default <KopflosConfig> {
baseIri: 'http://localhost:1429',
apiGraphs: ['http://localhost:1429/api'],
baseIri,
apiGraphs: [baseIri + '/api'],
sparql: {
default: {
endpointUrl: 'http://localhost:7878/query?union-default-graph',
updateUrl: 'http://localhost:7878/update',
storeUrl: 'http://localhost:7878/store',
endpointUrl: dbUri + '/query?union-default-graph',
updateUrl: dbUri + '/update',
storeUrl: dbUri + '/store',
},
},
watch: ['lib'],
Expand All @@ -28,7 +31,7 @@ export default <KopflosConfig> {
entrypoints: ['ui/*.html'],
},
'@kopflos-cms/hydra': {
apis: ['http://localhost:1429/api'],
apis: [baseIri + '/api'],
},
},
}
81 changes: 79 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/cli/lib/command/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default async function (args: BuildArgs) {
const plugins = await loadPlugins(config.plugins)

log.info('Running build actions...')
const buildActions = plugins.map(plugin => plugin.build?.())
const buildActions = plugins.map(Plugin => Plugin.build?.())
if (buildActions.length === 0) {
return log.warn('No plugins with build actions found')
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type { KopflosResponse, KopflosPlugin, PluginConfig, ResultEnvelope } from './lib/Kopflos.js'
export type { KopflosResponse, KopflosPlugin, PluginConfig, ResultEnvelope, KopflosPluginConstructor, Plugins } from './lib/Kopflos.js'
export type { Kopflos, KopflosConfig, Body, Query } from './lib/Kopflos.js'
export { default } from './lib/Kopflos.js'
export { loadHandlers as defaultHandlerLookup } from './lib/handler.js'
Expand Down
40 changes: 27 additions & 13 deletions packages/core/lib/Kopflos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,35 @@ export interface ResultEnvelope {

export type KopflosResponse = ResultBody | ResultEnvelope

export interface PluginConfig {
[plugin: string]: unknown
}

export interface KopflosPlugin {
readonly name?: string
onStart?(): Promise<void> | void
onStop?(): Promise<void> | void
apiTriples?(): Promise<DatasetCore | Stream> | DatasetCore | Stream
}

export interface Plugins extends Record<string, KopflosPlugin> {
}

export interface Kopflos<D extends DatasetCore = Dataset> {
get dataset(): D
get env(): KopflosEnvironment
get apis(): MultiPointer<Term, D>
// eslint-disable-next-line no-use-before-define
get plugins(): Array<KopflosPlugin>
get start(): () => Promise<void>
getPlugin<N extends keyof PluginConfig>(name: N): Plugins[N] | undefined
handleRequest(req: KopflosRequest<D>): Promise<ResultEnvelope>
loadApiGraphs(): Promise<void>
}

export interface KopflosPlugin {
export interface KopflosPluginConstructor {
new(instance: Kopflos): KopflosPlugin
build?: () => Promise<void> | void
onStart?(instance: Kopflos): Promise<void> | void
onStop?(instance: Kopflos): Promise<void> | void
apiTriples?(instance: Kopflos): Promise<DatasetCore | Stream> | DatasetCore | Stream
}

interface Clients {
Expand All @@ -81,10 +94,6 @@ interface Clients {

type Endpoint = string | EndpointOptions | Clients | Client

export interface PluginConfig {
[plugin: string]: unknown
}

export interface KopflosConfig {
[key: string]: unknown
mode?: 'development' | 'production'
Expand All @@ -101,7 +110,7 @@ export interface Options {
resourceShapeLookup?: ResourceShapeLookup
resourceLoaderLookup?: ResourceLoaderLookup
handlerLookup?: HandlerLookup
plugins?: Array<KopflosPlugin>
plugins?: Array<KopflosPluginConstructor>
}

export default class Impl implements Kopflos {
Expand All @@ -112,7 +121,7 @@ export default class Impl implements Kopflos {

constructor({ variables = {}, ...config }: KopflosConfig, private readonly options: Options = {}) {
this.env = createEnv({ variables, ...config })
this.plugins = options.plugins || []
this.plugins = (options.plugins || []).map(Plugin => new Plugin(this))

this.dataset = this.env.dataset([
...options.dataset || [],
Expand All @@ -134,7 +143,7 @@ export default class Impl implements Kopflos {
})

this.start = onetime(async function (this: Impl) {
await Promise.all(this.plugins.map(plugin => plugin.onStart?.(this)))
await Promise.all(this.plugins.map(plugin => plugin.onStart?.()))
}).bind(this)
}

Expand All @@ -146,6 +155,10 @@ export default class Impl implements Kopflos {
return this.graph.has(this.env.ns.rdf.type, this.env.ns.kopflos.Api)
}

getPlugin<N extends keyof Plugins>(name: N): Plugins[N] | undefined {
return this.plugins.find(plugin => plugin.name === name) as Plugins[N] | undefined
}

async getResponse(req: KopflosRequest<Dataset>): Promise<KopflosResponse | undefined | null> {
const resourceShapeMatch = await this.findResourceShape(req.iri)
if (isResponse(resourceShapeMatch)) {
Expand All @@ -169,6 +182,7 @@ export default class Impl implements Kopflos {
: {}
const args: HandlerArgs = {
...req,
instance: this,
headers: req.headers,
resourceShape,
env: this.env,
Expand Down Expand Up @@ -348,7 +362,7 @@ export default class Impl implements Kopflos {
return
}

const triples = await plugin.apiTriples(this)
const triples = await plugin.apiTriples()
for await (const quad of triples) {
this.dataset.add(quad)
}
Expand All @@ -366,6 +380,6 @@ export default class Impl implements Kopflos {
}

async stop() {
await Promise.all(this.plugins.map(async plugin => { plugin.onStop?.(this) }))
await Promise.all(this.plugins.map(async plugin => { plugin.onStop?.() }))
}
}
1 change: 1 addition & 0 deletions packages/core/lib/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { logCode } from './log.js'
type Dataset = ReturnType<KopflosEnvironment['dataset']>

export interface HandlerArgs<D extends DatasetCore = Dataset> {
instance: Kopflos
resourceShape: GraphPointer<NamedNode, D>
env: KopflosEnvironment
subject: GraphPointer<NamedNode, D>
Expand Down
Loading
Loading