diff --git a/apps/docs/docs/dev/04-guides/06-jayvee-extensions.md b/apps/docs/docs/dev/04-guides/06-jayvee-extensions.md index 2b212aa9..55dca917 100644 --- a/apps/docs/docs/dev/04-guides/06-jayvee-extensions.md +++ b/apps/docs/docs/dev/04-guides/06-jayvee-extensions.md @@ -48,7 +48,7 @@ import { JayveeExecExtension, } from '@jvalue/jayvee-execution'; -export class MyExecExtension implements JayveeExecExtension { +export class MyExecExtension extends JayveeExecExtension { getBlockExecutors(): BlockExecutorClass[] { return []; } @@ -72,7 +72,7 @@ In `libs/extensions/std/exec/src/extension.ts`: import { MyExecExtension } from '@jvalue/jayvee-extensions//exec'; -export class StdExecExtension implements JayveeExecExtension { +export class StdExecExtension extends JayveeExecExtension { private readonly wrappedExtensions: JayveeExecExtension[] = [ // ... // Register your execution extension here: @@ -177,7 +177,7 @@ In `libs/extensions//exec/src/extension.ts`: import { MyExtractorExecutor } from './lib/my-extractor-executor'; -export class MyExecExtension implements JayveeExecExtension { +export class MyExecExtension extends JayveeExecExtension { getBlockExecutors(): BlockExecutorClass[] { return [ // ... diff --git a/apps/docs/docs/dev/12-jayvee-testing.md b/apps/docs/docs/dev/12-jayvee-testing.md index 1121c1ef..1a5bada7 100644 --- a/apps/docs/docs/dev/12-jayvee-testing.md +++ b/apps/docs/docs/dev/12-jayvee-testing.md @@ -20,7 +20,7 @@ These kind of tests are mainly located inside the [language-server](https://gith The testing utils are located inside the `language-server` in a dedicated [test folder](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test). These utils can be imported using `@jvalue/jayvee-language-server/test` and contain the following parts: -[**langium-utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/langium-utils.ts): +[**langium-utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/langium-utils.ts): This utils file contains two functions: - `parseHelper` to simplify parsing the input (content of a *.jv file) and returning the corresponding `LangiumDocument`, and - `validationHelper` parse and validate the created document. @@ -100,34 +100,23 @@ pipeline Pipeline { ### Existing tests Currently there are already tests for the following parts: - Language-server validation checks (located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/validation)) -- Language-server constraint validation (located [here](https://github.com/jvalue/jayvee/tree/dev/libs/language-server/src/lib/constraint)) -- Custom block (property) validation of the three existing extensions (std extension located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src)) -- Grammar validation tests for all official full examples from the [/example](https://github.com/jvalue/jayvee/tree/main/example) folder (located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src/example-validation.spec.ts)) -- Grammar validation tests for all block examples of the std extension (located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src/meta-inf-example-validation.spec.ts)) +- Language-server constraint validation (located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/constraint)) +- Custom block (property) validation of the three existing extensions (std extension located [here](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src)) +- Grammar validation tests for all official full examples from the [/example](https://github.com/jvalue/jayvee/tree/main/example) folder (located [here](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src/example-validation.spec.ts)) +- Grammar validation tests for all block examples of the std extension (located [here](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src/meta-inf-example-validation.spec.ts)) ## Execution tests -These kind of tests are mainly located inside the [interpreter](https://github.com/jvalue/jayvee/tree/main/libs/language-server), the [interpreter-lib](https://github.com/jvalue/jayvee/tree/dev/libs/interpreter-lib), the [execution lib](https://github.com/jvalue/jayvee/tree/dev/libs/execution) as well as the execution parts of each extension (for example [std/exec](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/exec)). +These kind of tests are mainly located inside the [interpreter](https://github.com/jvalue/jayvee/tree/main/libs/language-server), the [interpreter-lib](https://github.com/jvalue/jayvee/tree/main/libs/interpreter-lib), the [execution lib](https://github.com/jvalue/jayvee/tree/main/libs/execution) as well as the execution parts of each extension (for example [std/exec](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/exec)). ### Testing utils -The testing utils for execution tests are spread between the extensions, with the interfaces and base utils located inside the [execution lib](https://github.com/jvalue/jayvee/tree/dev/libs/execution). +The testing utils for execution tests are spread between the extensions, with the interfaces and base utils located inside the [execution lib](https://github.com/jvalue/jayvee/tree/main/libs/execution). They can be imported using `@jvalue/jayvee-extensions/rdbms/test`, `@jvalue/jayvee-extensions/std/test` and `@jvalue/jayvee-execution/test`. -[**utils.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/utils.ts): -At the moment this only contains two functions: -- `clearBlockExecutorRegistry` for clearing the registry containing all `BlockExecutor`s, and -- `clearConstraintExecutorRegistry` clearing the corresponding `ConstraintExecutor`s registry. -They are required in case the tested method initializes Jayvee itself (see [smoke test](#existing-tests-1)). - -[**test-logger.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/test-logger.ts): -This contains a subclass of the [`DefaultLogger`](https://github.com/jvalue/jayvee/blob/dev/libs/execution/src/lib/logging/default-logger.ts) used for tests which require a `Logger` implementation. The `TestLogger` contains the following tests functionality: -- `getLogs`: retrieve the cached logs that the logger received. -- `clearLogs`: clear the cached logs. - -[**block-executor-mocks.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/block-executor-mock.ts): +[**block-executor-mocks.ts**](https://github.com/jvalue/jayvee/blob/main/libs/execution/test/block-executor-mock.ts): `BlockExecutorMock` interface for defining mocks for `AbstractBlockExecutor`. Generally only loader and executor blocks require mocks, because they interact with "the outside world" (i.e. `HttpExtractor` making http calls). Due to how vastly different each `BlockExecutor` can be, this interface is very simple, containing only a `setup(...args: unknown[])` and a `restore()` method. See below for existing implementations. -[**rdbms/exec/test**](https://github.com/jvalue/jayvee/tree/dev/libs/extensions/rdbms/exec/test): +[**rdbms/exec/test**](https://github.com/jvalue/jayvee/tree/main/libs/extensions/rdbms/exec/test): Contains the implementation of `BlockExecutorMock` for `PostgresLoaderExecutor` and `SQLiteLoaderExecutor`. Both of these executors are mocked using `jest.mock` to mock the corresponding libraries (`pg` and `sqlite3`) **Usage:** @@ -187,7 +176,7 @@ describe('Dummy describe', () => { }); ``` -[**std/exec/test/mocks**](https://github.com/jvalue/jayvee/tree/dev/libs/extensions/std/exec/test): +[**std/exec/test/mocks**](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/exec/test): Contains the implementation of `BlockExecutorMock` for `HttpExtractorExecutorMock`. This implementation uses [nock](https://www.npmjs.com/package/nock) for mocking HTTP(S) responses. The `setup` method is further specified requiring one parameter `registerMocks: () => Array`, which returns all used `nock.Scope` (i.e. the return value of `nock('')`), see usage below: @@ -251,4 +240,4 @@ describe('Dummy describe', () => { ### Existing tests Currently there are already tests for the following parts: -- Smoke test for official examples (located [here](https://github.com/jvalue/jayvee/blob/dev/apps/interpreter/src/examples-smoke-test.spec.ts)) +- Smoke test for official examples (located [here](https://github.com/jvalue/jayvee/blob/main/apps/interpreter/src/examples-smoke-test.spec.ts)) diff --git a/apps/interpreter/src/examples-smoke-test.spec.ts b/apps/interpreter/src/examples-smoke-test.spec.ts index 4baa3cba..43532847 100644 --- a/apps/interpreter/src/examples-smoke-test.spec.ts +++ b/apps/interpreter/src/examples-smoke-test.spec.ts @@ -4,11 +4,7 @@ import * as path from 'path'; -import { - clearBlockExecutorRegistry, - clearConstraintExecutorRegistry, - processExitMockImplementation, -} from '@jvalue/jayvee-execution/test'; +import { processExitMockImplementation } from '@jvalue/jayvee-execution/test'; import { PostgresLoaderExecutorMock, SQLiteLoaderExecutorMock, @@ -72,10 +68,6 @@ describe('jv example smoke tests', () => { httpExtractorMock.restore(); postgresLoaderMock.restore(); sqliteLoaderMock.restore(); - - // Clear registries - clearBlockExecutorRegistry(); - clearConstraintExecutorRegistry(); }); it('should have no errors when executing cars.jv example', async () => { diff --git a/apps/interpreter/src/parse-only.spec.ts b/apps/interpreter/src/parse-only.spec.ts index eb5abe1b..57a6dfbb 100644 --- a/apps/interpreter/src/parse-only.spec.ts +++ b/apps/interpreter/src/parse-only.spec.ts @@ -6,10 +6,6 @@ import * as fs from 'node:fs'; import * as path from 'path'; import * as process from 'process'; -import { - clearBlockExecutorRegistry, - clearConstraintExecutorRegistry, -} from '@jvalue/jayvee-execution/test'; import { RunOptions, interpretModel, @@ -52,10 +48,6 @@ describe('Parse Only', () => { jest.spyOn(process, 'exit').mockImplementation(() => { throw new Error(); }); - - // Reset jayvee specific stuff - clearBlockExecutorRegistry(); - clearConstraintExecutorRegistry(); }); it('should exit with 0 on a valid option', async () => { diff --git a/libs/execution/src/lib/blocks/block-execution-util.ts b/libs/execution/src/lib/blocks/block-execution-util.ts index 57e27972..42ea1c7d 100644 --- a/libs/execution/src/lib/blocks/block-execution-util.ts +++ b/libs/execution/src/lib/blocks/block-execution-util.ts @@ -9,12 +9,10 @@ import { PipelineWrapper, } from '@jvalue/jayvee-language-server'; -import { ExecutionContext } from '../execution-context'; +import { type ExecutionContext } from '../execution-context'; import { Logger } from '../logging/logger'; import { IOTypeImplementation, NONE } from '../types'; -// eslint-disable-next-line import/no-cycle -import { createBlockExecutor } from './block-executor-registry'; import * as R from './execution-result'; export interface ExecutionOrderItem { @@ -94,7 +92,8 @@ export async function executeBlock( return R.ok(null); } - const blockExecutor = createBlockExecutor(block); + const blockExecutor = + executionContext.executionExtension.createBlockExecutor(block); const startTime = new Date(); diff --git a/libs/execution/src/lib/blocks/block-executor-registry.ts b/libs/execution/src/lib/blocks/block-executor-registry.ts deleted file mode 100644 index 98ae0174..00000000 --- a/libs/execution/src/lib/blocks/block-executor-registry.ts +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg -// -// SPDX-License-Identifier: AGPL-3.0-only - -import { strict as assert } from 'assert'; - -import { - BlockDefinition, - Registry, - isCompositeBlocktypeDefinition, -} from '@jvalue/jayvee-language-server'; - -import { BlockExecutor } from './block-executor'; -import { BlockExecutorClass } from './block-executor-class'; -// eslint-disable-next-line import/no-cycle -import { - createCompositeBlockExecutor, - getInputType, - getOutputType, -} from './composite-block-executor'; - -export const blockExecutorRegistry = new Registry(); - -export function registerBlockExecutor(executorClass: BlockExecutorClass) { - blockExecutorRegistry.register(executorClass.type, executorClass); -} - -export function getRegisteredBlockExecutors(): BlockExecutorClass[] { - return [...blockExecutorRegistry.getAll()]; -} - -export function createBlockExecutor(block: BlockDefinition): BlockExecutor { - const blockType = block.type.ref; - assert(blockType !== undefined); - - if ( - !blockExecutorRegistry.get(blockType.name) && - isCompositeBlocktypeDefinition(block.type.ref) - ) { - const executorClass = createCompositeBlockExecutor( - getInputType(block.type.ref), - getOutputType(block.type.ref), - block, - ); - - blockExecutorRegistry.register(block.type.ref.name, executorClass); - } - - const blockExecutor = blockExecutorRegistry.get(blockType.name); - - assert( - blockExecutor !== undefined, - `No executor was registered for block type ${blockType.name}`, - ); - - return new blockExecutor(); -} diff --git a/libs/execution/src/lib/blocks/block-executor.ts b/libs/execution/src/lib/blocks/block-executor.ts index 9f013565..aac8e7a2 100644 --- a/libs/execution/src/lib/blocks/block-executor.ts +++ b/libs/execution/src/lib/blocks/block-executor.ts @@ -8,7 +8,7 @@ import { IOType, isBlockDefinition } from '@jvalue/jayvee-language-server'; import { isBlockTargetedForDebugLogging } from '../debugging/debug-configuration'; import { DebugLogVisitor } from '../debugging/debug-log-visitor'; -import { ExecutionContext } from '../execution-context'; +import { type ExecutionContext } from '../execution-context'; import { IOTypeImplementation } from '../types/io-types/io-type-implementation'; import * as R from './execution-result'; diff --git a/libs/execution/src/lib/blocks/composite-block-executor.ts b/libs/execution/src/lib/blocks/composite-block-executor.ts index f2fcf637..72b67bb2 100644 --- a/libs/execution/src/lib/blocks/composite-block-executor.ts +++ b/libs/execution/src/lib/blocks/composite-block-executor.ts @@ -20,10 +20,9 @@ import { isCompositeBlocktypeDefinition, } from '@jvalue/jayvee-language-server'; -import { ExecutionContext } from '../execution-context'; -import { IOTypeImplementation } from '../types'; +import { type ExecutionContext } from '../execution-context'; +import { type IOTypeImplementation } from '../types'; -// eslint-disable-next-line import/no-cycle import { executeBlocks } from './block-execution-util'; import { AbstractBlockExecutor, BlockExecutor } from './block-executor'; import { BlockExecutorClass } from './block-executor-class'; diff --git a/libs/execution/src/lib/blocks/index.ts b/libs/execution/src/lib/blocks/index.ts index a693022b..614c184f 100644 --- a/libs/execution/src/lib/blocks/index.ts +++ b/libs/execution/src/lib/blocks/index.ts @@ -4,6 +4,5 @@ export * from './block-executor'; export * from './block-executor-class'; -export * from './block-executor-registry'; export * from './execution-result'; export * from './block-execution-util'; diff --git a/libs/execution/src/lib/constraints/constraint-executor-extension.ts b/libs/execution/src/lib/constraints/constraint-executor-extension.ts new file mode 100644 index 00000000..aab4d44c --- /dev/null +++ b/libs/execution/src/lib/constraints/constraint-executor-extension.ts @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { strict as assert } from 'assert'; + +import { + ConstraintDefinition, + Registry, + isExpressionConstraintDefinition, + isTypedConstraintDefinition, +} from '@jvalue/jayvee-language-server'; +import { assertUnreachable } from 'langium'; + +import { ConstraintExecutor } from './constraint-executor'; +import { AllowlistConstraintExecutor } from './executors/allowlist-constraint-executor'; +import { DenylistConstraintExecutor } from './executors/denylist-constraint-executor'; +import { ExpressionConstraintExecutor } from './executors/expression-constraint-executor'; +import { LengthConstraintExecutor } from './executors/length-constraint-executor'; +import { RangeConstraintExecutor } from './executors/range-constraint-executor'; +import { RegexConstraintExecutor } from './executors/regex-constraint-executor'; +import { TypedConstraintExecutorClass } from './typed-constraint-executor-class'; + +export interface JayveeConstraintExtension { + registerConstraintExecutor(executorClass: TypedConstraintExecutorClass): void; + + getConstraintExecutors(): TypedConstraintExecutorClass[]; + + createConstraintExecutor( + constraint: ConstraintDefinition, + ): ConstraintExecutor; +} + +export class DefaultConstraintExtension + extends Registry + implements JayveeConstraintExtension +{ + constructor() { + super(); + + this.registerConstraintExecutor(AllowlistConstraintExecutor); + this.registerConstraintExecutor(DenylistConstraintExecutor); + this.registerConstraintExecutor(RegexConstraintExecutor); + this.registerConstraintExecutor(LengthConstraintExecutor); + this.registerConstraintExecutor(RangeConstraintExecutor); + } + + registerConstraintExecutor(executorClass: TypedConstraintExecutorClass) { + this.register(executorClass.type, executorClass); + } + + getConstraintExecutors() { + return this.getAll(); + } + + createConstraintExecutor( + constraint: ConstraintDefinition, + ): ConstraintExecutor { + if (isTypedConstraintDefinition(constraint)) { + const constraintType = constraint.type.ref?.name; + assert( + constraintType !== undefined, + `Could not resolve reference to constraint type of ${constraint.name}`, + ); + const constraintExecutor = this.get(constraintType); + assert( + constraintExecutor !== undefined, + `No executor was registered for constraint type ${constraintType}`, + ); + + return new constraintExecutor(); + } else if (isExpressionConstraintDefinition(constraint)) { + return new ExpressionConstraintExecutor(constraint); + } + assertUnreachable(constraint); + } +} diff --git a/libs/execution/src/lib/constraints/constraint-executor-registry.ts b/libs/execution/src/lib/constraints/constraint-executor-registry.ts deleted file mode 100644 index b8c9a1ea..00000000 --- a/libs/execution/src/lib/constraints/constraint-executor-registry.ts +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg -// -// SPDX-License-Identifier: AGPL-3.0-only - -import { strict as assert } from 'assert'; - -import { - ConstraintDefinition, - Registry, - isExpressionConstraintDefinition, - isTypedConstraintDefinition, -} from '@jvalue/jayvee-language-server'; -import { assertUnreachable } from 'langium'; - -import { ConstraintExecutor } from './constraint-executor'; -import { AllowlistConstraintExecutor } from './executors/allowlist-constraint-executor'; -import { DenylistConstraintExecutor } from './executors/denylist-constraint-executor'; -import { ExpressionConstraintExecutor } from './executors/expression-constraint-executor'; -import { LengthConstraintExecutor } from './executors/length-constraint-executor'; -import { RangeConstraintExecutor } from './executors/range-constraint-executor'; -import { RegexConstraintExecutor } from './executors/regex-constraint-executor'; -import { TypedConstraintExecutorClass } from './typed-constraint-executor-class'; - -export const constraintExecutorRegistry = - new Registry(); - -export function registerDefaultConstraintExecutors() { - registerConstraintExecutor(AllowlistConstraintExecutor); - registerConstraintExecutor(DenylistConstraintExecutor); - registerConstraintExecutor(RegexConstraintExecutor); - registerConstraintExecutor(LengthConstraintExecutor); - registerConstraintExecutor(RangeConstraintExecutor); -} - -export function registerConstraintExecutor( - executorClass: TypedConstraintExecutorClass, -) { - constraintExecutorRegistry.register(executorClass.type, executorClass); -} - -export function getRegisteredConstraintExecutors(): TypedConstraintExecutorClass[] { - return constraintExecutorRegistry.getAll(); -} - -export function createConstraintExecutor( - constraint: ConstraintDefinition, -): ConstraintExecutor { - if (isTypedConstraintDefinition(constraint)) { - const constraintType = constraint.type.ref?.name; - assert( - constraintType !== undefined, - `Could not resolve reference to constraint type of ${constraint.name}`, - ); - const constraintExecutor = constraintExecutorRegistry.get(constraintType); - assert( - constraintExecutor !== undefined, - `No executor was registered for constraint type ${constraintType}`, - ); - - return new constraintExecutor(); - } else if (isExpressionConstraintDefinition(constraint)) { - return new ExpressionConstraintExecutor(constraint); - } - assertUnreachable(constraint); -} diff --git a/libs/execution/src/lib/constraints/constraint-executor.ts b/libs/execution/src/lib/constraints/constraint-executor.ts index b2f3947f..6a262d91 100644 --- a/libs/execution/src/lib/constraints/constraint-executor.ts +++ b/libs/execution/src/lib/constraints/constraint-executor.ts @@ -4,7 +4,7 @@ import { InternalValueRepresentation } from '@jvalue/jayvee-language-server'; -import { ExecutionContext } from '../execution-context'; +import { type ExecutionContext } from '../execution-context'; export interface ConstraintExecutor { isValid( diff --git a/libs/execution/src/lib/constraints/default-constraint-executors.spec.ts b/libs/execution/src/lib/constraints/default-constraint-executors-extension.spec.ts similarity index 66% rename from libs/execution/src/lib/constraints/default-constraint-executors.spec.ts rename to libs/execution/src/lib/constraints/default-constraint-executors-extension.spec.ts index fd3e0049..78fff87b 100644 --- a/libs/execution/src/lib/constraints/default-constraint-executors.spec.ts +++ b/libs/execution/src/lib/constraints/default-constraint-executors-extension.spec.ts @@ -9,26 +9,22 @@ import { } from '@jvalue/jayvee-language-server'; import { NodeFileSystem } from 'langium/node'; -import { - getRegisteredConstraintExecutors, - registerDefaultConstraintExecutors, -} from './constraint-executor-registry'; +import { DefaultConstraintExtension } from './constraint-executor-extension'; -describe('default constraint executors', () => { +describe('default constraint extension', () => { it('should include executors for all constraint types', async () => { // Create language services const services = createJayveeServices(NodeFileSystem).Jayvee; await initializeWorkspace(services); - registerDefaultConstraintExecutors(); + const defaultConstraintExtension = new DefaultConstraintExtension(); getAllBuiltinConstraintTypes( services.shared.workspace.LangiumDocuments, ).forEach((constraintType) => { - const matchingConstraintExecutorClass = - getRegisteredConstraintExecutors().find( - (c) => c.type === constraintType.type, - ); + const matchingConstraintExecutorClass = defaultConstraintExtension + .getConstraintExecutors() + .find((c) => c.type === constraintType.type); expect(matchingConstraintExecutorClass).toBeDefined(); }); diff --git a/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.ts b/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.ts index 6fea440a..eb05889c 100644 --- a/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.ts +++ b/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.ts @@ -8,7 +8,7 @@ import { PrimitiveValuetypes, } from '@jvalue/jayvee-language-server'; -import { ExecutionContext } from '../../execution-context'; +import { type ExecutionContext } from '../../execution-context'; import { implementsStatic } from '../../util/implements-static-decorator'; import { ConstraintExecutor } from '../constraint-executor'; import { TypedConstraintExecutorClass } from '../typed-constraint-executor-class'; diff --git a/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.ts b/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.ts index 48a86c9c..2f62b138 100644 --- a/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.ts +++ b/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.ts @@ -8,7 +8,7 @@ import { PrimitiveValuetypes, } from '@jvalue/jayvee-language-server'; -import { ExecutionContext } from '../../execution-context'; +import { type ExecutionContext } from '../../execution-context'; import { implementsStatic } from '../../util/implements-static-decorator'; import { ConstraintExecutor } from '../constraint-executor'; import { TypedConstraintExecutorClass } from '../typed-constraint-executor-class'; diff --git a/libs/execution/src/lib/constraints/executors/expression-constraint-executor.ts b/libs/execution/src/lib/constraints/executors/expression-constraint-executor.ts index c7baf65f..8efe7b9a 100644 --- a/libs/execution/src/lib/constraints/executors/expression-constraint-executor.ts +++ b/libs/execution/src/lib/constraints/executors/expression-constraint-executor.ts @@ -12,7 +12,7 @@ import { evaluateExpression, } from '@jvalue/jayvee-language-server'; -import { ExecutionContext } from '../../execution-context'; +import { type ExecutionContext } from '../../execution-context'; import { ConstraintExecutor } from '../constraint-executor'; export class ExpressionConstraintExecutor diff --git a/libs/execution/src/lib/constraints/executors/length-constraint-executor.ts b/libs/execution/src/lib/constraints/executors/length-constraint-executor.ts index c2f5d411..24e9f17b 100644 --- a/libs/execution/src/lib/constraints/executors/length-constraint-executor.ts +++ b/libs/execution/src/lib/constraints/executors/length-constraint-executor.ts @@ -7,7 +7,7 @@ import { PrimitiveValuetypes, } from '@jvalue/jayvee-language-server'; -import { ExecutionContext } from '../../execution-context'; +import { type ExecutionContext } from '../../execution-context'; import { implementsStatic } from '../../util/implements-static-decorator'; import { ConstraintExecutor } from '../constraint-executor'; import { TypedConstraintExecutorClass } from '../typed-constraint-executor-class'; diff --git a/libs/execution/src/lib/constraints/executors/range-constraint-executor.ts b/libs/execution/src/lib/constraints/executors/range-constraint-executor.ts index 8ae6a7d5..93e92eea 100644 --- a/libs/execution/src/lib/constraints/executors/range-constraint-executor.ts +++ b/libs/execution/src/lib/constraints/executors/range-constraint-executor.ts @@ -7,7 +7,7 @@ import { PrimitiveValuetypes, } from '@jvalue/jayvee-language-server'; -import { ExecutionContext } from '../../execution-context'; +import { type ExecutionContext } from '../../execution-context'; import { implementsStatic } from '../../util/implements-static-decorator'; import { ConstraintExecutor } from '../constraint-executor'; import { TypedConstraintExecutorClass } from '../typed-constraint-executor-class'; diff --git a/libs/execution/src/lib/constraints/executors/regex-constraint-executor.ts b/libs/execution/src/lib/constraints/executors/regex-constraint-executor.ts index e9c77c5a..ff2aacd1 100644 --- a/libs/execution/src/lib/constraints/executors/regex-constraint-executor.ts +++ b/libs/execution/src/lib/constraints/executors/regex-constraint-executor.ts @@ -7,7 +7,7 @@ import { PrimitiveValuetypes, } from '@jvalue/jayvee-language-server'; -import { ExecutionContext } from '../../execution-context'; +import { type ExecutionContext } from '../../execution-context'; import { implementsStatic } from '../../util/implements-static-decorator'; import { ConstraintExecutor } from '../constraint-executor'; import { TypedConstraintExecutorClass } from '../typed-constraint-executor-class'; diff --git a/libs/execution/src/lib/constraints/index.ts b/libs/execution/src/lib/constraints/index.ts index 4a4cf0d5..35f9ce9a 100644 --- a/libs/execution/src/lib/constraints/index.ts +++ b/libs/execution/src/lib/constraints/index.ts @@ -2,4 +2,4 @@ // // SPDX-License-Identifier: AGPL-3.0-only -export * from './constraint-executor-registry'; +export * from './constraint-executor-extension'; diff --git a/libs/execution/src/lib/debugging/debug-log-visitor.ts b/libs/execution/src/lib/debugging/debug-log-visitor.ts index 00a1fa07..f387d112 100644 --- a/libs/execution/src/lib/debugging/debug-log-visitor.ts +++ b/libs/execution/src/lib/debugging/debug-log-visitor.ts @@ -5,7 +5,7 @@ import { internalValueToString } from '@jvalue/jayvee-language-server'; import { Logger } from '../logging/logger'; -import { Workbook } from '../types'; +import { type Workbook } from '../types'; import { FileSystem } from '../types/io-types/filesystem'; import { BinaryFile } from '../types/io-types/filesystem-node-file-binary'; import { TextFile } from '../types/io-types/filesystem-node-file-text'; diff --git a/libs/execution/src/lib/execution-context.ts b/libs/execution/src/lib/execution-context.ts index 3f535efe..7c072ea3 100644 --- a/libs/execution/src/lib/execution-context.ts +++ b/libs/execution/src/lib/execution-context.ts @@ -25,10 +25,12 @@ import { } from '@jvalue/jayvee-language-server'; import { assertUnreachable, isReference } from 'langium'; +import { JayveeConstraintExtension } from './constraints'; import { DebugGranularity, DebugTargets, } from './debugging/debug-configuration'; +import { type JayveeExecExtension } from './extension'; import { Logger } from './logging/logger'; export type StackNode = @@ -41,6 +43,8 @@ export class ExecutionContext { constructor( public readonly pipeline: PipelineDefinition, + public readonly executionExtension: JayveeExecExtension, + public readonly constraintExtension: JayveeConstraintExtension, public readonly logger: Logger, public readonly runOptions: { isDebugMode: boolean; diff --git a/libs/execution/src/lib/extension.ts b/libs/execution/src/lib/extension.ts index 1f620318..6cb62ce2 100644 --- a/libs/execution/src/lib/extension.ts +++ b/libs/execution/src/lib/extension.ts @@ -2,13 +2,54 @@ // // SPDX-License-Identifier: AGPL-3.0-only +import { strict as assert } from 'assert'; + +import { + BlockDefinition, + isCompositeBlocktypeDefinition, +} from '@jvalue/jayvee-language-server'; + +import { type BlockExecutor } from './blocks'; import { BlockExecutorClass } from './blocks/block-executor-class'; -import { registerBlockExecutor } from './blocks/block-executor-registry'; +import { + createCompositeBlockExecutor, + getInputType, + getOutputType, +} from './blocks/composite-block-executor'; -export interface JayveeExecExtension { - getBlockExecutors(): BlockExecutorClass[]; -} +export abstract class JayveeExecExtension { + abstract getBlockExecutors(): BlockExecutorClass[]; + + getExecutorForBlockType( + blockTypeName: string, + ): BlockExecutorClass | undefined { + return this.getBlockExecutors().find( + (x: BlockExecutorClass) => x.type === blockTypeName, + ); + } + + createBlockExecutor(block: BlockDefinition): BlockExecutor { + const blockType = block.type.ref; + assert(blockType !== undefined); + + let blockExecutor = this.getExecutorForBlockType(blockType.name); + + if ( + blockExecutor === undefined && + isCompositeBlocktypeDefinition(block.type.ref) + ) { + blockExecutor = createCompositeBlockExecutor( + getInputType(block.type.ref), + getOutputType(block.type.ref), + block, + ); + } + + assert( + blockExecutor !== undefined, + `No executor was registered for block type ${blockType.name}`, + ); -export function useExtension(extension: JayveeExecExtension) { - extension.getBlockExecutors().forEach(registerBlockExecutor); + return new blockExecutor(); + } } diff --git a/libs/execution/src/lib/types/valuetypes/value-representation-validity.ts b/libs/execution/src/lib/types/valuetypes/value-representation-validity.ts index dc5d04b0..4642d7e8 100644 --- a/libs/execution/src/lib/types/valuetypes/value-representation-validity.ts +++ b/libs/execution/src/lib/types/valuetypes/value-representation-validity.ts @@ -22,8 +22,7 @@ import { ValuetypeVisitor, } from '@jvalue/jayvee-language-server'; -import { createConstraintExecutor } from '../../constraints/constraint-executor-registry'; -import { ExecutionContext } from '../../execution-context'; +import { type ExecutionContext } from '../../execution-context'; export function isValidValueRepresentation( value: InternalValueRepresentation, @@ -53,7 +52,8 @@ class ValueRepresentationValidityVisitor extends ValuetypeVisitor { this.context.evaluationContext, ); for (const constraint of constraints) { - const constraintExecutor = createConstraintExecutor(constraint); + const constraintExecutor = + this.context.constraintExtension.createConstraintExecutor(constraint); this.context.enterNode(constraint); const valueFulfilledConstraint = constraintExecutor.isValid( diff --git a/libs/execution/test/utils/test-infrastructure-util.ts b/libs/execution/test/utils/test-infrastructure-util.ts index 1565bf60..3896db24 100644 --- a/libs/execution/test/utils/test-infrastructure-util.ts +++ b/libs/execution/test/utils/test-infrastructure-util.ts @@ -10,23 +10,22 @@ import { import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; import { + BlockExecutorClass, CachedLogger, DebugGranularity, DebugTargets, + DefaultConstraintExtension, ExecutionContext, + JayveeExecExtension, StackNode, Table, TableColumn, - blockExecutorRegistry, - constraintExecutorRegistry, } from '../../src'; -export function clearBlockExecutorRegistry() { - blockExecutorRegistry.clear(); -} - -export function clearConstraintExecutorRegistry() { - constraintExecutorRegistry.clear(); +export class TestExecExtension extends JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return []; + } } export function processExitMockImplementation(code?: number) { @@ -58,6 +57,8 @@ export function getTestExecutionContext( const executionContext = new ExecutionContext( pipeline, + new TestExecExtension(), + new DefaultConstraintExtension(), new CachedLogger(runOptions.isDebugMode, undefined, loggerPrintLogs), runOptions, new EvaluationContext(new RuntimeParameterProvider()), diff --git a/libs/extensions/rdbms/exec/src/extension.ts b/libs/extensions/rdbms/exec/src/extension.ts index 675a82ac..8d8a2f30 100644 --- a/libs/extensions/rdbms/exec/src/extension.ts +++ b/libs/extensions/rdbms/exec/src/extension.ts @@ -9,7 +9,7 @@ import { import { PostgresLoaderExecutor, SQLiteLoaderExecutor } from './lib'; -export class RdbmsExecExtension implements JayveeExecExtension { +export class RdbmsExecExtension extends JayveeExecExtension { getBlockExecutors(): BlockExecutorClass[] { return [PostgresLoaderExecutor, SQLiteLoaderExecutor]; } diff --git a/libs/extensions/std/exec/src/extension.ts b/libs/extensions/std/exec/src/extension.ts index 86bf3526..5c3213d0 100644 --- a/libs/extensions/std/exec/src/extension.ts +++ b/libs/extensions/std/exec/src/extension.ts @@ -18,7 +18,7 @@ import { TextFileInterpreterExecutor } from './text-file-interpreter-executor'; import { TextLineDeleterExecutor } from './text-line-deleter-executor'; import { TextRangeSelectorExecutor } from './text-range-selector-executor'; -export class StdExecExtension implements JayveeExecExtension { +export class StdExecExtension extends JayveeExecExtension { private readonly wrappedExtensions: JayveeExecExtension[] = [ new TabularExecExtension(), new RdbmsExecExtension(), diff --git a/libs/extensions/tabular/exec/src/extension.ts b/libs/extensions/tabular/exec/src/extension.ts index 80746ca9..06b01623 100644 --- a/libs/extensions/tabular/exec/src/extension.ts +++ b/libs/extensions/tabular/exec/src/extension.ts @@ -17,7 +17,7 @@ import { TableInterpreterExecutor } from './lib/table-interpreter-executor'; import { TableTransformerExecutor } from './lib/table-transformer-executor'; import { XLSXInterpreterExecutor } from './lib/xlsx-interpreter-executor'; -export class TabularExecExtension implements JayveeExecExtension { +export class TabularExecExtension extends JayveeExecExtension { getBlockExecutors(): BlockExecutorClass[] { return [ CellWriterExecutor, diff --git a/libs/interpreter-lib/src/interpreter.ts b/libs/interpreter-lib/src/interpreter.ts index 1662ab15..b2ee632b 100644 --- a/libs/interpreter-lib/src/interpreter.ts +++ b/libs/interpreter-lib/src/interpreter.ts @@ -7,14 +7,15 @@ import { strict as assert } from 'assert'; import * as R from '@jvalue/jayvee-execution'; import { DebugGranularity, + DefaultConstraintExtension, ExecutionContext, + JayveeConstraintExtension, + JayveeExecExtension, Logger, executeBlocks, isDebugGranularity, logExecutionDuration, parseValueToInternalRepresentation, - registerDefaultConstraintExecutors, - useExtension as useExecutionExtension, } from '@jvalue/jayvee-execution'; import { StdExecExtension } from '@jvalue/jayvee-extensions/std/exec'; import { @@ -98,9 +99,6 @@ export async function parseModel( return { model, services, loggerFactory }; } - useStdExtension(); - registerDefaultConstraintExecutors(); - services = createJayveeServices(NodeFileSystem).Jayvee; await initializeWorkspace(services); setupJayveeServices(services, options.env); @@ -136,6 +134,8 @@ export async function interpretModel( const interpretationExitCode = await interpretJayveeModel( model, + new StdExecExtension(), + new DefaultConstraintExtension(), services.RuntimeParameterProvider, loggerFactory, { @@ -173,12 +173,10 @@ function setupRuntimeParameterProvider( } } -export function useStdExtension() { - useExecutionExtension(new StdExecExtension()); -} - async function interpretJayveeModel( model: JayveeModel, + executionExtension: JayveeExecExtension, + constraintExtension: JayveeConstraintExtension, runtimeParameterProvider: RuntimeParameterProvider, loggerFactory: LoggerFactory, runOptions: InterpreterOptions, @@ -186,6 +184,8 @@ async function interpretJayveeModel( const pipelineRuns: Promise[] = model.pipelines.map((pipeline) => { return runPipeline( pipeline, + executionExtension, + constraintExtension, runtimeParameterProvider, loggerFactory, runOptions, @@ -201,12 +201,16 @@ async function interpretJayveeModel( async function runPipeline( pipeline: PipelineDefinition, + executionExtension: JayveeExecExtension, + constraintExtension: JayveeConstraintExtension, runtimeParameterProvider: RuntimeParameterProvider, loggerFactory: LoggerFactory, runOptions: InterpreterOptions, ): Promise { const executionContext = new ExecutionContext( pipeline, + executionExtension, + constraintExtension, loggerFactory.createLogger(), { isDebugMode: runOptions.debug, diff --git a/libs/interpreter-lib/src/std-extension.spec.ts b/libs/interpreter-lib/src/std-extension.spec.ts index 05bb58d1..413c7dda 100644 --- a/libs/interpreter-lib/src/std-extension.spec.ts +++ b/libs/interpreter-lib/src/std-extension.spec.ts @@ -4,7 +4,7 @@ import { strict as assert } from 'assert'; -import { getRegisteredBlockExecutors } from '@jvalue/jayvee-execution'; +import { StdExecExtension } from '@jvalue/jayvee-extensions/std/exec'; import { BlockTypeWrapper, createJayveeServices, @@ -13,8 +13,6 @@ import { } from '@jvalue/jayvee-language-server'; import { NodeFileSystem } from 'langium/node'; -import { useStdExtension } from './interpreter'; - async function loadAllBuiltinBlocktypes(): Promise { const services = createJayveeServices(NodeFileSystem).Jayvee; await initializeWorkspace(services); @@ -23,13 +21,12 @@ async function loadAllBuiltinBlocktypes(): Promise { describe('std extension', () => { it('should provide matching block executors for builtin block types', async () => { - useStdExtension(); (await loadAllBuiltinBlocktypes()).forEach( (blockType: BlockTypeWrapper) => { + const execExtension = new StdExecExtension(); console.info(`Looking for executor for blocktype ${blockType.type}`); - const matchingBlockExecutorClass = getRegisteredBlockExecutors().find( - (blockExecutorClass) => blockExecutorClass.type === blockType.type, - ); + const matchingBlockExecutorClass = + execExtension.getExecutorForBlockType(blockType.type); expect(matchingBlockExecutorClass).toBeDefined(); assert(matchingBlockExecutorClass !== undefined); diff --git a/libs/language-server/src/lib/util/registry.ts b/libs/language-server/src/lib/util/registry.ts index d0657186..7ba731af 100644 --- a/libs/language-server/src/lib/util/registry.ts +++ b/libs/language-server/src/lib/util/registry.ts @@ -5,9 +5,9 @@ import { strict as assert } from 'assert'; export class Registry { - private readonly registry = new Map(); + protected readonly registry = new Map(); - register(key: string, classToRegister: C) { + protected register(key: string, classToRegister: C) { assert( !this.registry.has(key), `Multiple keys "${key}" were registered, expected at most one register call per key`, @@ -15,21 +15,21 @@ export class Registry { this.registry.set(key, classToRegister); } - getAll(): C[] { + protected getAll(): C[] { return [...this.registry.values()]; } - getAllEntries(): { key: string; value: C }[] { + protected getAllEntries(): { key: string; value: C }[] { return [...this.registry.entries()].map(([k, v]) => { return { key: k, value: v }; }); } - get(key: string): C | undefined { + protected get(key: string): C | undefined { return this.registry.get(key); } - clear() { + protected clear() { this.registry.clear(); } }