Skip to content

Commit

Permalink
Initial commit with package structure for shacl template generator
Browse files Browse the repository at this point in the history
  • Loading branch information
ddvlanck committed Mar 14, 2024
1 parent b049549 commit 01d5f62
Show file tree
Hide file tree
Showing 11 changed files with 431 additions and 0 deletions.
31 changes: 31 additions & 0 deletions packages/oslo-generator-shacl-template/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# `OSLO SHACL Template Generator`

> Generates a SHACL template based on an OSLO JSON-LD file
## Install

```bash
npm install @oslo-flanders/shacl-template-generator
```

## Global install

```bash
npm install -g @oslo-flanders/shacl-template-generator
```

## API
| Parameter | Description | Required | Possible values |
| ---------------- | ----------------------------------------------------------- | --------------------------------- | ------------------------------------------- |
| `--input` | The path of an OSLO JSON-LD file | :heavy_check_mark: | |
| `--output` | Name of the output file | No, default `shacl.jsonld` | |
| `--language` | The language in which to generate the SHACL template | :heavy_check_mark: | |
| `--shapeBaseURI` | The base URI to be used for the HTTP URIs of the SHACL shapes | No, default `http://example.org` ||
| `--mode` | The generation mode | No, default `grouped` | `grouped` or `individual` |
| `--constraints` | Additional constraints to add to the SHACL shapes | No | `stringsNotEmpty`, `uniqueLanguages` or `nodeKind`. Multiple constraint are allowed |
| `--applicationProfileURL` | The URL on which the application profile is published, to create cross-references | No | |
| `--useUniqueURIs` | Create unique HTTP URIs for the individual SHACL shapes using the labels | No, default `false` | |
| `--addCodelistRules` | Add rules for codelists, if present | No, default `false` | |
| `--addConstraintMessages` | Add additional messages in the configured language to the SHACL shapes | No, default `false` ||
| `--addConstraintRuleNumbers` | Add extra entry for rule numbers, allowing editors to add a rule numbers across multiple specs | No, default `false` ||

9 changes: 9 additions & 0 deletions packages/oslo-generator-shacl-template/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'reflect-metadata'
import 'module-alias/register';

export * from '@oslo-generator-shacl-template/ShaclTemplateGenerationServiceRunner';
export * from '@oslo-generator-shacl-template/ShaclTemplateGenerationService';
export * from '@oslo-generator-shacl-template/config/ShaclTemplateGenerationServiceConfiguration';
export * from '@oslo-generator-shacl-template/enums/Constraint';
export * from '@oslo-generator-shacl-template/enums/GenerationMode';
export * from '@oslo-generator-shacl-template/utils/utils';
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { IService, Logger, QuadStore, ServiceIdentifier } from "@oslo-flanders/core";
import { ShaclTemplateGenerationServiceConfiguration } from "./config/ShaclTemplateGenerationServiceConfiguration";
import { inject, injectable } from "inversify";

@injectable()
export class ShaclTemplateGenerationService implements IService {
public readonly logger: Logger;
public readonly configuration: ShaclTemplateGenerationServiceConfiguration;
public readonly store: QuadStore;

public constructor(
@inject(ServiceIdentifier.Logger) logger: Logger,
@inject(ServiceIdentifier.Configuration) config: ShaclTemplateGenerationServiceConfiguration,
@inject(ServiceIdentifier.QuadStore) store: QuadStore,
) {
this.logger = logger;
this.configuration = config;
this.store = store;
}

public async init(): Promise<void> {
return this.store.addQuadsFromFile(this.configuration.input);
}

public async run(): Promise<void> {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { CliArgv } from "@oslo-flanders/core";
import { AppRunner } from "@oslo-flanders/core";
import yargs from "yargs";
import { container } from "@oslo-generator-shacl-template/config/DependencyInjectionConfig";
import type { ShaclTemplateGenerationServiceConfiguration }
from "@oslo-generator-shacl-template/config/ShaclTemplateGenerationServiceConfiguration";
import type { ShaclTemplateGenerationService } from "@oslo-generator-shacl-template/ShaclTemplateGenerationService";

export class ShaclTemplateGenerationServiceRunner extends
AppRunner<ShaclTemplateGenerationService, ShaclTemplateGenerationServiceConfiguration> {

public async runCli(argv: CliArgv): Promise<void> {
const yargv = yargs(argv.slice(2))
.usage('node ./bin/runner.js [args]')
.option('input', { describe: 'The input file to generate a SHACL template from.' })
.option('output', { describe: 'Name of the output file.', default: 'shacl.jsonld' })
.option('language', { describe: 'The language in which the SHACL template must be generated.' })
.option('shapeBaseURI', {
describe: 'The base URI to be used for the HTTP URIs of the SHACL shapes.',
default: 'http://example.org',
})
.option('mode', {
describe: 'The generation mode, which can be \'grouped\' or \'individual\'.',
default: 'grouped',
choices: ['grouped', 'individual'],
})
.option('constraints', {
describe: 'Additional constraints to add to the SHACL shape.',
type: 'array',
default: [],
choices: ['stringsNotEmpty', 'uniqueLanguages', 'nodeKind'],
})
.option('applicationProfileURL', {
describe: `The URL on which the application profile is published.
This is needed to create references to terms on the application profile.`,
default: '',
})
.option('useUniqueURIs', {
describe: 'Create unique HTTP URIs for the individual SHACL shapes using the labels',
default: false,
boolean: true,
})
.option('addCodelistRules', {
describe: 'Add rules for codelists, if they are present',
default: false,
boolean: true,
})
.option('addConstraintMessages', {
describe: 'Add additional messages in the configured language to the SHACL shapes',
default: false,
boolean: true,
})
.option('addRuleNumbers', {
describe: 'Add extra entry for rule numbers, allowing editors to add a rule number across multiple specs.',
default: false,
boolean: true,
})
.option('silent',
{
describe: 'All logs are suppressed',
default: false,
boolean: true,
})
.demandOption(['input', 'language'])
.help('h')
.alias('h', 'help');

const params = await yargv.parse();
this.startApp(params, container).catch(error => console.error(error));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { IService, IConfiguration } from "@oslo-flanders/core";
import { QuadStore, ServiceIdentifier } from "@oslo-flanders/core";
import { Container } from "inversify";
import { ShaclTemplateGenerationServiceConfiguration }
from "@oslo-generator-shacl-template/config/ShaclTemplateGenerationServiceConfiguration";
import { ShaclTemplateGenerationService } from "@oslo-generator-shacl-template/ShaclTemplateGenerationService";

export const container = new Container();

container.bind<IService>(ServiceIdentifier.Service)
.to(ShaclTemplateGenerationService)
.inSingletonScope();

container.bind<IConfiguration>(ServiceIdentifier.Configuration)
.to(ShaclTemplateGenerationServiceConfiguration)
.inSingletonScope();

container.bind<QuadStore>(ServiceIdentifier.QuadStore)
.to(QuadStore);
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { IConfiguration, YargsParams } from "@oslo-flanders/core";
import type { GenerationMode } from "@oslo-generator-shacl-template/enums/GenerationMode";
import type { Constraint } from "@oslo-generator-shacl-template/enums/Constraint";
import { getConstraints, getGenerationMode } from "@oslo-generator-shacl-template/utils/utils";

export class ShaclTemplateGenerationServiceConfiguration implements IConfiguration {
/**
* Path of the input file.
*/
private _input: string | undefined;

/**
* Path of the output file.
*/
private _output: string | undefined;

/**
* Language to generate the template in.
*/
private _language: string | undefined;

/**
* The base URI to be used for the HTTP URIs of the SHACL shapes.
*/
private _shapeBaseURI: string | undefined;

/**
* The generation mode, which can be 'grouped' or 'individual'.
* @see GenerationMode
*/
private _mode: GenerationMode | undefined;

/**
* Additional constraints to add to the SHACL shape.
* @see Constraint
*/
private _constraints: Constraint[] | undefined;

/**
* The URL on which the application profile is published. This is needed to create references to terms on the application profile
*/
private _applicationProfileURL: string | undefined;

/**
* Create unique HTTP URIs for the individual SHACL shapes using the labels
*/
private _useUniqueURIs: boolean | undefined;

/**
* Add rules for codelists, if they are present
*/
private _addCodelistRules: boolean | undefined;

/**
* Add additional messages in the configured language to the SHACL shapes
*/
private _addConstraintMessages: boolean | undefined;

/**
* Add extra entry for rule numbers, allowing editors to add a rule number across multiple specs.
*/
private _addConstraintRuleNumbers: boolean | undefined;

public async createFromCli(params: YargsParams): Promise<void> {
this._input = <string>params.input;
this._output = <string>params.output;
this._language = <string>params.language;
this._shapeBaseURI = <string>params.shapeBaseURI;
this._mode = getGenerationMode(<string>params.mode);
this._constraints = getConstraints(<string[]>params.constraints);
this._applicationProfileURL = <string>params.applicationProfileURL;
this._useUniqueURIs = <boolean>params.uniqueURIs;
this._addCodelistRules = <boolean>params.addCodelistRules;
this._addConstraintMessages = <boolean>params.addConstraintMessages;
this._addConstraintRuleNumbers = <boolean>params.addRuleNumbers;
}

public get input(): string {
if (!this._input) {
throw new Error(`Trying to access "input" before it was set.`)
}
return this._input;
}

public get output(): string {
if (!this._output) {
throw new Error(`Trying to access "output" before it was set.`)
}
return this._output;
}

public get language(): string {
if (!this._language) {
throw new Error(`Trying to access "language" before it was set.`)
}
return this._language;
}

public get shapeBaseURI(): string {
if (!this._shapeBaseURI) {
throw new Error(`Trying to access "shapeBaseURI" before it was set.`)
}
return this._shapeBaseURI;
}

public get mode(): GenerationMode {
if (!this._mode) {
throw new Error(`Trying to access "mode" before it was set.`)
}
return this._mode;
}

public get constraints(): Constraint[] {
if (!this._constraints) {
throw new Error(`Trying to access "constraints" before it was set.`)
}
return this._constraints;
}

public get applicationProfileURL(): string {
if (!this._applicationProfileURL) {
throw new Error(`Trying to access "applicationProfileURL" before it was set.`)
}
return this._applicationProfileURL;
}

public get useUniqueURIs(): boolean {
if (!this._useUniqueURIs) {
throw new Error(`Trying to access "useUniqueURIs" before it was set.`)
}
return this._useUniqueURIs;
}

public get addCodelistRules(): boolean {
if (!this._addCodelistRules) {
throw new Error(`Trying to access "addCodelistRules" before it was set.`)
}
return this._addCodelistRules;
}

public get addConstraintMessages(): boolean {
if (!this._addConstraintMessages) {
throw new Error(`Trying to access "addConstraintMessages" before it was set.`)
}
return this._addConstraintMessages;
}

public get addConstraintRuleNumbers() {
if (!this._addConstraintRuleNumbers) {
throw new Error(`Trying to access "addConstraintRuleNumbers" before it was set.`)
}
return this._addConstraintRuleNumbers;
}
}
16 changes: 16 additions & 0 deletions packages/oslo-generator-shacl-template/lib/enums/Constraint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export enum Constraint {
/**
* Add an extra constraint that the value of a property can not be empty, otherwise generate an error
*/
StringsNotEmpty,

/**
* Allow one value per language
*/
UniqueLanguage,

/**
* Add constraint that the range of the property should be a sh:IRI of sh:Literal, depending on its type
*/
NodeKind
}
12 changes: 12 additions & 0 deletions packages/oslo-generator-shacl-template/lib/enums/GenerationMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export enum GenerationMode {
/**
* Generate one SHACL shape per class which includes all properties of the class
*/
Grouped,

/**
* Generate one SHACL shape per entity (class and properties).
* For each constraint on the class or property, there is a separate SHACL shape
*/
Individual
}
32 changes: 32 additions & 0 deletions packages/oslo-generator-shacl-template/lib/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Constraint } from "@oslo-generator-shacl-template/enums/Constraint";
import { GenerationMode } from "@oslo-generator-shacl-template/enums/GenerationMode";

export function getGenerationMode(mode: string): GenerationMode {
switch (mode) {
case "grouped":
return GenerationMode.Grouped;
case "individual":
return GenerationMode.Individual;
default:
throw new Error(`Generation mode '${mode}' is not supported.`);
}
}

export function getConstraints(constraintStrings: string[] | undefined): Constraint[] {
if (!constraintStrings) {
return [];
}

return constraintStrings.map((constraintString) => {
switch (constraintString) {
case "stringsNotEmpty":
return Constraint.StringsNotEmpty;
case "uniqueLanguage":
return Constraint.UniqueLanguage;
case "nodeKind":
return Constraint.NodeKind;
default:
throw new Error(`Constraint '${constraintString}' is not supported.`);
}
});
}
Loading

0 comments on commit 01d5f62

Please sign in to comment.