Skip to content

Commit

Permalink
Merge pull request #9 from maartyman/implement-pod
Browse files Browse the repository at this point in the history
Implement pod
  • Loading branch information
maartyman authored Feb 8, 2024
2 parents 05054e2 + 9f39646 commit a1589a7
Show file tree
Hide file tree
Showing 18 changed files with 4,464 additions and 221 deletions.
28 changes: 19 additions & 9 deletions config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,31 @@
"sa:config/endpoint/endpoint.json"
],
"@graph": [
{
"@id": "urn:solid-aggregator:cost-queue-factory",
"@type": "CostQueueTimeFactory"
},
{
"@id": "urn:solid-aggregator:fetch",
"@type": "NativeFetch"
},
{
"@id": "urn:solid-aggregator:pod",
"@type": "PodCss"
},
{
"@id": "urn:solid-aggregator:aggregator-service-aggregation",
"@type": "ServiceAggregation",
"fetch": { "@id": "urn:solid-aggregator:fetch" },
"pod": { "@id": "urn:solid-aggregator:pod" }
},
{
"@id": "urn:solid-aggregator:service-registry",
"@type": "ServiceRegistryHardcodedTestOnly",
"costQueueFactory": { "@id": "urn:solid-aggregator:cost-queue-factory" },
"aggregatorServices": [
{ "@id": "urn:solid-aggregator:aggregator-service-SPARQL" }
{ "@id": "urn:solid-aggregator:aggregator-service-aggregation" }
]
},
{
"@id": "urn:solid-aggregator:aggregator-service-SPARQL",
"@type": "ServiceSparql"
},
{
"@id": "urn:solid-aggregator:cost-queue-factory",
"@type": "CostQueueTimeFactory"
}
]
}
29 changes: 29 additions & 0 deletions lib/core/AsyncConstructor.ts
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;
3 changes: 3 additions & 0 deletions lib/fetch/IFetch.ts
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>;
}
7 changes: 7 additions & 0 deletions lib/fetch/NativeFetch.ts
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);
}
}
11 changes: 10 additions & 1 deletion lib/index.ts
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';
2 changes: 1 addition & 1 deletion lib/init/AppRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class AppRunner {
const manager = await ComponentsManager.build({
mainModulePath: path.join(__dirname, '../..'),
});
await manager.configRegistry.register('../../config/default.json');
await manager.configRegistry.register('config/default.json');

const endpoint: Endpoint = await manager.instantiate('urn:solid-aggregator:endpoint');
await endpoint.start();
Expand Down
7 changes: 7 additions & 0 deletions lib/pod/IPod.ts
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

View workflow job for this annotation

GitHub Actions / lint / lint

'"../service/IService"' has no exported member named 'ServiceDescription'. Did you mean 'IServiceDescription'?

Check failure on line 1 in lib/pod/IPod.ts

View workflow job for this annotation

GitHub Actions / test / test (ubuntu-latest, 18.x)

'"../service/IService"' has no exported member named 'ServiceDescription'. Did you mean 'IServiceDescription'?

Check failure on line 1 in lib/pod/IPod.ts

View workflow job for this annotation

GitHub Actions / test / test (ubuntu-latest, 20.x)

'"../service/IService"' has no exported member named 'ServiceDescription'. Did you mean 'IServiceDescription'?

export interface IPod {
newServiceLocation: (description: ServiceDescription) => Promise<string>;
}

export type PodServiceLocation = string;
64 changes: 64 additions & 0 deletions lib/pod/PodCss.ts
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

View workflow job for this annotation

GitHub Actions / lint / lint

'"../service/IService"' has no exported member named 'ServiceDescription'. Did you mean 'IServiceDescription'?

Check failure on line 6 in lib/pod/PodCss.ts

View workflow job for this annotation

GitHub Actions / test / test (ubuntu-latest, 18.x)

'"../service/IService"' has no exported member named 'ServiceDescription'. Did you mean 'IServiceDescription'?

Check failure on line 6 in lib/pod/PodCss.ts

View workflow job for this annotation

GitHub Actions / test / test (ubuntu-latest, 20.x)

'"../service/IService"' has no exported member named 'ServiceDescription'. Did you mean 'IServiceDescription'?
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}`);
}
}
1 change: 1 addition & 0 deletions lib/pod/assets/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./podData
37 changes: 37 additions & 0 deletions lib/pod/assets/css-config.json
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": []
}
9 changes: 9 additions & 0 deletions lib/pod/assets/seed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[
{
"email": "[email protected]",
"password": "aggregator",
"pods": [
{ "name": "aggregator" }
]
}
]
5 changes: 3 additions & 2 deletions lib/service-registry/ServiceRegistryHardcodedTestOnly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export class ServiceRegistryHardcodedTestOnly implements IServiceRegistry {
public async initializeServices(): Promise<void> {
await Promise.all(
this.services.map(
async(aggregatorService): Promise<void> => aggregatorService.initialize(),
async(aggregatorService): Promise<void> => new Promise<void>((resolve): void =>
aggregatorService.subscribeInitialized(resolve)),
),
);
}
Expand Down Expand Up @@ -46,7 +47,7 @@ export class ServiceRegistryHardcodedTestOnly implements IServiceRegistry {
public get descriptions(): string[] {
const result = [];
for (const service of this.services) {
result.push(service.description);
result.push(service.description.toString());
}
return result;
}
Expand Down
10 changes: 7 additions & 3 deletions lib/service/IService.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import type { CostParameters } from '../cost-queue/ICostQueue';
import type { IAsyncConstructor } from '../core/AsyncConstructor';

export interface IService {
get description(): string;
initialize: () => Promise<void>;
export interface IService extends IAsyncConstructor {
get description(): IServiceDescription;
test: (operation: IOperation) => Promise<IOperationTestResult>;
run: (operation: IOperation) => Promise<IOperationResult | undefined>;
}

export interface IServiceDescription {
toString: () => string;
}

export interface IOperationTestResult {
aggregatorService: IService;
operation: IOperation;
Expand Down
70 changes: 70 additions & 0 deletions lib/service/ServiceAggregation.ts
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

View workflow job for this annotation

GitHub Actions / lint / lint

'"./IService"' has no exported member named 'Operation'. Did you mean 'IOperation'?

Check failure on line 6 in lib/service/ServiceAggregation.ts

View workflow job for this annotation

GitHub Actions / lint / lint

'"./IService"' has no exported member named 'OperationResult'. Did you mean 'IOperationResult'?

Check failure on line 6 in lib/service/ServiceAggregation.ts

View workflow job for this annotation

GitHub Actions / lint / lint

'"./IService"' has no exported member named 'OperationTestResult'. Did you mean 'IOperationTestResult'?

Check failure on line 6 in lib/service/ServiceAggregation.ts

View workflow job for this annotation

GitHub Actions / lint / lint

'"./IService"' has no exported member named 'ServiceDescription'. Did you mean 'IServiceDescription'?

Check failure on line 6 in lib/service/ServiceAggregation.ts

View workflow job for this annotation

GitHub Actions / test / test (ubuntu-latest, 18.x)

'"./IService"' has no exported member named 'Operation'. Did you mean 'IOperation'?

Check failure on line 6 in lib/service/ServiceAggregation.ts

View workflow job for this annotation

GitHub Actions / test / test (ubuntu-latest, 18.x)

'"./IService"' has no exported member named 'OperationResult'. Did you mean 'IOperationResult'?

Check failure on line 6 in lib/service/ServiceAggregation.ts

View workflow job for this annotation

GitHub Actions / test / test (ubuntu-latest, 18.x)

'"./IService"' has no exported member named 'OperationTestResult'. Did you mean 'IOperationTestResult'?

Check failure on line 6 in lib/service/ServiceAggregation.ts

View workflow job for this annotation

GitHub Actions / test / test (ubuntu-latest, 18.x)

'"./IService"' has no exported member named 'ServiceDescription'. Did you mean 'IServiceDescription'?

Check failure on line 6 in lib/service/ServiceAggregation.ts

View workflow job for this annotation

GitHub Actions / test / test (ubuntu-latest, 20.x)

'"./IService"' has no exported member named 'Operation'. Did you mean 'IOperation'?

Check failure on line 6 in lib/service/ServiceAggregation.ts

View workflow job for this annotation

GitHub Actions / test / test (ubuntu-latest, 20.x)

'"./IService"' has no exported member named 'OperationResult'. Did you mean 'IOperationResult'?

Check failure on line 6 in lib/service/ServiceAggregation.ts

View workflow job for this annotation

GitHub Actions / test / test (ubuntu-latest, 20.x)

'"./IService"' has no exported member named 'OperationTestResult'. Did you mean 'IOperationTestResult'?

Check failure on line 6 in lib/service/ServiceAggregation.ts

View workflow job for this annotation

GitHub Actions / test / test (ubuntu-latest, 20.x)

'"./IService"' has no exported member named 'ServiceDescription'. Did you mean 'IServiceDescription'?

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;
};
Loading

0 comments on commit a1589a7

Please sign in to comment.