-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
Implement pod
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
export interface IAsyncConstructor { | ||
subscribeInitialized: (callback: () => void) => void; | ||
} | ||
|
||
export abstract class AsyncConstructor implements IAsyncConstructor { | ||
public initialized = false; | ||
private readonly callbacks: (() => void)[] = []; | ||
|
||
protected constructor(args: AsyncConstructorArgs) { | ||
this.initialize(args) | ||
.then((): void => { | ||
this.initialized = true; | ||
for (const callback of this.callbacks) { | ||
callback(); | ||
} | ||
}) | ||
.catch((reason): void => { | ||
throw new Error(`Error during async initialization of class '${this.constructor.name}', reason: "${reason}".`); | ||
}); | ||
} | ||
|
||
protected abstract initialize(args: AsyncConstructorArgs): Promise<void>; | ||
|
||
public subscribeInitialized(callback: () => void): void { | ||
this.callbacks.push(callback); | ||
} | ||
} | ||
|
||
export type AsyncConstructorArgs = any; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export interface IFetch { | ||
fetch: (input: string | URL | Request, init?: RequestInit) => Promise<Response>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import type { IFetch } from './IFetch'; | ||
|
||
export class NativeFetch implements IFetch { | ||
public async fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> { | ||
return fetch(input, init); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,22 @@ | ||
export * from './core/AsyncConstructor'; | ||
|
||
export * from './cost-queue/ICostQueue'; | ||
export * from './cost-queue/CostQueueTime'; | ||
|
||
export * from './endpoint/Endpoint'; | ||
export * from './endpoint/EndpointHandlerServiceDescription'; | ||
|
||
export * from './fetch/IFetch'; | ||
export * from './fetch/NativeFetch'; | ||
|
||
export * from './init/AppRunner'; | ||
|
||
export * from './pod/IPod'; | ||
export * from './pod/PodCss'; | ||
|
||
export * from './service/IService'; | ||
export * from './service/ServiceSparql'; | ||
export * from './service/ServiceEmpty'; | ||
export * from './service/ServiceAggregation'; | ||
|
||
export * from './service-registry/IServiceRegistry'; | ||
export * from './service-registry/ServiceRegistryHardcodedTestOnly'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import type { ServiceDescription } from '../service/IService'; | ||
Check failure on line 1 in lib/pod/IPod.ts GitHub Actions / lint / lint
Check failure on line 1 in lib/pod/IPod.ts GitHub Actions / test / test (ubuntu-latest, 18.x)
|
||
|
||
export interface IPod { | ||
newServiceLocation: (description: ServiceDescription) => Promise<string>; | ||
} | ||
|
||
export type PodServiceLocation = string; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import * as path from 'node:path'; | ||
import * as fs from 'fs-extra'; | ||
import { AppRunner } from '@solid/community-server'; | ||
import { v4 } from 'uuid'; | ||
import { AsyncConstructor } from '../core/AsyncConstructor'; | ||
import type { ServiceDescription } from '../service/IService'; | ||
Check failure on line 6 in lib/pod/PodCss.ts GitHub Actions / lint / lint
Check failure on line 6 in lib/pod/PodCss.ts GitHub Actions / test / test (ubuntu-latest, 18.x)
|
||
import type { IPod, PodServiceLocation } from './IPod'; | ||
|
||
export class PodCss extends AsyncConstructor implements IPod { | ||
public podURL = 'http://localhost:3000/aggregator'; | ||
|
||
public constructor() { | ||
super({}); | ||
} | ||
|
||
protected async initialize(): Promise<void> { | ||
// TODO [2024-03-01]: make sure the file for the server is selected => not sure actually | ||
|
||
const loaderProperties = { | ||
mainModulePath: 'node_modules/@solid/community-server/', | ||
dumpErrorState: true, | ||
typeChecking: false, | ||
}; | ||
|
||
const config = path.join(__dirname, './assets/css-config.json'); | ||
|
||
const shorthand: Record<string, unknown> = { | ||
rootFilePath: path.join(__dirname, './assets/podData/'), | ||
}; | ||
if (!(await fs.pathExists(path.join(__dirname, './assets/podData/')))) { | ||
shorthand.seedConfig = path.join(__dirname, './assets/seed.json'); | ||
} | ||
|
||
await (new AppRunner()).run({ | ||
loaderProperties, | ||
config, | ||
shorthand, | ||
}); | ||
// TODO [2024-03-01]: Edit profile card | ||
} | ||
|
||
public async newServiceLocation(description: ServiceDescription): Promise<PodServiceLocation> { | ||
if (!this.initialized) { | ||
await new Promise<void>((resolve): void => { | ||
this.subscribeInitialized((): void => { | ||
resolve(); | ||
}); | ||
}); | ||
} | ||
|
||
// Create service folder with uuid | ||
const location = `${this.podURL}/${v4()}`; | ||
const response = await fetch(`${location}/description`, { | ||
method: 'PUT', | ||
body: description.toString(), | ||
}); | ||
|
||
if (response.ok) { | ||
return location; | ||
} | ||
// TODO [2024-03-01]: redo maybe? | ||
throw new Error(`Can't create location on the Solid Server: ${response.statusText}`); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
./podData |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", | ||
"import": [ | ||
"css:config/app/init/default.json", | ||
"css:config/app/main/default.json", | ||
"css:config/app/variables/default.json", | ||
"css:config/http/handler/default.json", | ||
"css:config/http/middleware/default.json", | ||
"css:config/http/notifications/all.json", | ||
"css:config/http/server-factory/http.json", | ||
"css:config/http/static/default.json", | ||
"css:config/identity/access/public.json", | ||
"css:config/identity/email/default.json", | ||
"css:config/identity/handler/default.json", | ||
"css:config/identity/oidc/default.json", | ||
"css:config/identity/ownership/token.json", | ||
"css:config/identity/pod/static.json", | ||
"css:config/ldp/authentication/dpop-bearer.json", | ||
"css:config/ldp/authorization/allow-all.json", | ||
"css:config/ldp/handler/default.json", | ||
"css:config/ldp/metadata-parser/default.json", | ||
"css:config/ldp/metadata-writer/default.json", | ||
"css:config/ldp/modes/default.json", | ||
"css:config/storage/backend/memory.json", | ||
"css:config/storage/key-value/resource-store.json", | ||
"css:config/storage/location/pod.json", | ||
"css:config/storage/middleware/default.json", | ||
"css:config/util/auxiliary/empty.json", | ||
"css:config/util/identifiers/suffix.json", | ||
"css:config/util/index/default.json", | ||
"css:config/util/logging/winston.json", | ||
"css:config/util/representation-conversion/default.json", | ||
"css:config/util/resource-locker/file.json", | ||
"css:config/util/variables/default.json" | ||
], | ||
"@graph": [] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[ | ||
{ | ||
"email": "[email protected]", | ||
"password": "aggregator", | ||
"pods": [ | ||
{ "name": "aggregator" } | ||
] | ||
} | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { StreamParser, StreamWriter } from 'n3'; | ||
import { v4 } from 'uuid'; | ||
import type { IFetch } from '../fetch/IFetch'; | ||
import type { IPod } from '../pod/IPod'; | ||
import { AsyncConstructor } from '../core/AsyncConstructor'; | ||
import type { IService, Operation, OperationResult, OperationTestResult, ServiceDescription } from './IService'; | ||
Check failure on line 6 in lib/service/ServiceAggregation.ts GitHub Actions / lint / lint
Check failure on line 6 in lib/service/ServiceAggregation.ts GitHub Actions / lint / lint
Check failure on line 6 in lib/service/ServiceAggregation.ts GitHub Actions / lint / lint
Check failure on line 6 in lib/service/ServiceAggregation.ts GitHub Actions / lint / lint
Check failure on line 6 in lib/service/ServiceAggregation.ts GitHub Actions / test / test (ubuntu-latest, 18.x)
Check failure on line 6 in lib/service/ServiceAggregation.ts GitHub Actions / test / test (ubuntu-latest, 18.x)
Check failure on line 6 in lib/service/ServiceAggregation.ts GitHub Actions / test / test (ubuntu-latest, 18.x)
Check failure on line 6 in lib/service/ServiceAggregation.ts GitHub Actions / test / test (ubuntu-latest, 18.x)
Check failure on line 6 in lib/service/ServiceAggregation.ts GitHub Actions / test / test (ubuntu-latest, 20.x)
Check failure on line 6 in lib/service/ServiceAggregation.ts GitHub Actions / test / test (ubuntu-latest, 20.x)
Check failure on line 6 in lib/service/ServiceAggregation.ts GitHub Actions / test / test (ubuntu-latest, 20.x)
|
||
|
||
export class ServiceAggregation extends AsyncConstructor implements IService { | ||
public fetch: IFetch; | ||
public pod: IPod; | ||
private podLocation: string | undefined; | ||
|
||
public constructor(args: ServiceAggregationArgs) { | ||
super(args); | ||
this.fetch = args.fetch; | ||
this.pod = args.pod; | ||
} | ||
|
||
protected async initialize(args: ServiceAggregationArgs): Promise<void> { | ||
this.podLocation = await args.pod.newServiceLocation(this.description); | ||
} | ||
|
||
public async test(operation: Operation): Promise<OperationTestResult> { | ||
if (operation.operation !== 'Aggregation') { | ||
throw new Error('Not an aggregation operation'); | ||
} | ||
return { | ||
aggregatorService: this, | ||
operation, | ||
runnable: true, | ||
}; | ||
} | ||
|
||
public async run(operation: Operation): Promise<OperationResult> { | ||
const resultLocation = `${this.podLocation}/${v4()}.ttl`; | ||
|
||
const streamWriter = new StreamWriter(); | ||
for (const source of operation.sources) { | ||
const streamParser = new StreamParser(); | ||
// eslint-disable-next-line ts/no-unsafe-argument | ||
(await this.fetch.fetch(source)).body?.pipeThrough(streamParser as any); | ||
streamParser.pipe(streamWriter); | ||
} | ||
|
||
await this.fetch.fetch(resultLocation, { | ||
method: 'PUT', | ||
body: streamWriter, | ||
headers: { | ||
'Content-Type': 'text/turtle', // eslint-disable-line ts/naming-convention | ||
}, | ||
}); | ||
|
||
return { | ||
aggregatorService: this, | ||
operation, | ||
resultLocation, | ||
}; | ||
} | ||
|
||
public get description(): ServiceDescription { | ||
return { | ||
toString: (): string => 'Aggregation', | ||
}; | ||
} | ||
} | ||
|
||
export type ServiceAggregationArgs = { | ||
fetch: IFetch; | ||
pod: IPod; | ||
}; |