Skip to content

Commit

Permalink
Merge pull request #23 from dxfrontier/feature/onError
Browse files Browse the repository at this point in the history
  • Loading branch information
dragolea authored Jan 22, 2024
2 parents c307314 + 3f1a724 commit 10e75e6
Show file tree
Hide file tree
Showing 18 changed files with 1,285 additions and 563 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,8 @@ jobs:
- name: Instal SAP CDS-DK
run: npm i -g @sap/cds-dk ts-node

- name: Instal Globally typescript
run: npm i -g typescript

- name: Start server and start e2e tests
run: npm run test:live-server:e2e-tests
124 changes: 95 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ The goal of CDS-TS-Dispatcher is to significantly reduce the boilerplate code re
- [OnAction](#onaction)
- [OnFunction](#onfunction)
- [OnEvent](#onevent)
- [OnError](#onerror)
- [OnBoundAction](#onboundaction)
- [OnBoundFunction](#onboundfunction)
- [Methods : `draft entity`](#methods--draft-entity)
Expand Down Expand Up @@ -294,10 +295,14 @@ The `CDSDispatcher` constructor allows you to create an instance for dispatching
import { CDSDispatcher } from '@dxfrontier/cds-ts-dispatcher';

module.exports = new CDSDispatcher([
// Entities
BookHandler,
ReviewHandler,
BookStatsHandler,
// Draft
BookEventsHandler,
// Unbound actions
UnboundActionsHandler,
// ...
]).initialize();
```

Expand Down Expand Up @@ -328,10 +333,11 @@ import { EntityHandler } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@EntityHandler(MyEntity)
class CustomerHandler {
...
export class CustomerHandler {
// ...
constructor() {}
...
// ...
}
```

> [!NOTE]
Expand All @@ -353,10 +359,11 @@ When applying `ServiceLogic` decorator, the class becomes eligible to be used wi
import { ServiceLogic } from '@dxfrontier/cds-ts-dispatcher';

@ServiceLogic()
class CustomerService {
...
export class CustomerService {
// ...
constructor() {}
...
// ...
}
```

<p align="right">(<a href="#table-of-contents">back to top</a>)</p>
Expand All @@ -373,10 +380,11 @@ When applying `Repository` decorator, the class becomes eligible to be used with
import { Repository } from '@dxfrontier/cds-ts-dispatcher';

@Repository()
class CustomerRepository {
...
export class CustomerRepository {
// ...
constructor() {}
...
// ...
}
```

###### `[Optional]` - BaseRepository
Expand All @@ -390,7 +398,7 @@ It simplifies the implementation by offering a set of ready-to-use actions for i
- `.find()`: Query the database to find specific data.
- `.delete()`: Remove records from the database.
- `.exists()`: Check the existence of data in the database.
- ... and many other actions
- and many more ...

To get started, refer to the official documentation **[BaseRepository](https://github.com/dxfrontier/cds-ts-repository)**. Explore the capabilities it offers and enhance your data access layer with ease.

Expand All @@ -403,7 +411,7 @@ import { BaseRepository } from '@dxfrontier/cds-ts-repository';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@Repository()
class CustomerRepository extends BaseRepository<MyEntity> {
export class CustomerRepository extends BaseRepository<MyEntity> {
constructor() {
super(MyEntity);
}
Expand All @@ -428,6 +436,13 @@ class CustomerRepository extends BaseRepository<MyEntity> {

The `@UnboundActions` decorator is utilized at the `class-level` to annotate a `class` as a specialized class which will be used only for Unbound actions.

The following decorators can be used inside of `@UnboundActions()` :

- [@OnAction](#onaction)
- [@OnFunction](#onfunction)
- [@OnEvent](#onevent)
- [@OnError](#onerror)

`Example`

```typescript
Expand All @@ -439,12 +454,13 @@ import {
ActionRequest,
ActionReturn,
TypedRequest,
Request,
} from '@dxfrontier/cds-ts-dispatcher';
import { MyAction, MyFunction, MyEvent } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@UnboundActions()
class UnboundActionsHandler {
// ... @Inject all dependencies, if needed.
export class UnboundActionsHandler {
// ... @Inject dependencies, if needed.

constructor() {}

Expand All @@ -468,6 +484,12 @@ class UnboundActionsHandler {
public async onEventMethod(req: TypedRequest<MyEvent>) {
// ...
}

// Unbound error
@OnError()
public onErrorMethod(err: Error, req: Request) {
// ...
}
}
```

Expand Down Expand Up @@ -503,13 +525,17 @@ import { EntityHandler, Inject, SRV, Service } from "@dxfrontier/cds-ts-dispatch
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@EntityHandler(MyEntity)
class CustomerHandler {
export class CustomerHandler {
...
@Inject(CustomerService) private customerService: CustomerService
@Inject(CustomerRepository) private customerService: CustomerRepository
@Inject(AnyOtherInjectableClass) private repository: AnyOtherInjectableClass

@Inject(SRV) private srv: Service
...
// ...
constructor() {}
...
// ...
}
```

> [!NOTE]
Expand All @@ -526,19 +552,20 @@ This specialized `@Inject` can be used as a `constant` in `@ServiceLogic, @Repos
`Example`

```typescript
import { EntityHandler, Inject, SRV, Service } from "@dxfrontier/cds-ts-dispatcher";
import { EntityHandler, Inject, SRV, Service } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@EntityHandler(MyEntity)
// OR @ServiceLogic()
// OR @Repository()
// OR @UnboundActions
class CustomerHandler { // OR CustomerService, CustomerRepository
...
@Inject(SRV) private srv: Service
...
export class CustomerHandler {
// @Inject dependencies
@Inject(SRV) private srv: Service;

constructor() {}
...
// ...
}
```

> [!NOTE]
Expand All @@ -558,7 +585,7 @@ The handlers receive one argument:

See also the official SAP JS **[CDS-Before](https://cap.cloud.sap/docs/node.js/core-services#srv-before-request) event**

> [!NOTE]
> [!TIP]
> If `@odata.draft.enabled: true` to manage event handlers for draft version you can use `@BeforeCreateDraft(), BeforeReadDraft(), @BeforeUpdateDraft(), @BeforeDeleteDraft()`
<p align="right">(<a href="#table-of-contents">back to top</a>)</p>
Expand Down Expand Up @@ -700,7 +727,7 @@ The handlers receive two arguments:

See also the official SAP JS **[CDS-After](https://cap.cloud.sap/docs/node.js/core-services#srv-after-request) event**

> [!NOTE]
> [!TIP]
> If `@odata.draft.enabled: true` to manage event handlers for draft version you can use `@AfterCreateDraft(), AfterReadDraft(), @AfterUpdateDraft(), @AfterDeleteDraft()`
<p align="right">(<a href="#table-of-contents">back to top</a>)</p>
Expand Down Expand Up @@ -840,7 +867,7 @@ The handlers receive two arguments:

See also the official SAP JS **[CDS-On](https://cap.cloud.sap/docs/node.js/core-services#srv-on-request) event**

> [!NOTE]
> [!TIP]
> If `@odata.draft.enabled: true` to manage event handlers for draft version you can use `@OnCreateDraft(), @OnReadDraft(), @OnUpdateDraft(), @OnDeleteDraft(), @OnBoundActionDraft(), @OnBoundFunctionDraft()`
<p align="right">(<a href="#table-of-contents">back to top</a>)</p>
Expand Down Expand Up @@ -1077,6 +1104,43 @@ this.on('AEvent', async (req) => {
<p align="right">(<a href="#table-of-contents">back to top</a>)</p>

###### OnError

**@OnError**()

Use `@OnError` decorator to register custom error handler.

Error handlers are invoked whenever an error occurs during event processing of all potential events and requests, and are used to augment or modify error messages, before they go out to clients.

`Example`

```typescript
import { OnError, Request } from "@dxfrontier/cds-ts-dispatcher";

@OnError()
public onError(err: Error, req: Request) { // sync func
err.message = 'New message'
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.on('error', async (err, req) => {
err.message = 'New message';
// ...
});
```

> [!CAUTION]
> OnError callback are expected to be a **`sync`** function, i.e., **`not async`**, not returning `Promises`.
> [!TIP]
> More info can be found at <https://cap.cloud.sap/docs/node.js/core-services#srv-on-error>
<p align="right">(<a href="#table-of-contents">back to top</a>)</p>

###### OnBoundAction

**@OnBoundAction**(`name` : CdsAction)
Expand Down Expand Up @@ -1586,7 +1650,7 @@ this.on('SAVE', MyEntity, async (req, next) => {
All active entity [On](#on), [Before](#before), [After](#after) events have also a `Draft` variant.

> [!NOTE]
> Except the `@OnAction(), @OnFunction()` as this are bound to the service and not to an entity.
> Except the `@OnAction(), @OnFunction(), @OnEvent(), @OnError()` as this are bound to the service and not to an entity.
<p align="right">(<a href="#table-of-contents">back to top</a>)</p>

Expand Down Expand Up @@ -1685,11 +1749,13 @@ npm install --save-dev npm-run-all
- npx @cap-js/cds-typer "*" --outputDirectory gen/srv/@cds-models
```
`Steps explained`

- `npm ci` - Will do a clean install
- `npm run build:production` - will run the package.json script command for CDS build and transpilation of TS to JS
- `npm run build:production` - will run the package.json script command for CDS build and transpilation of TS to JS and clean the `TS files`.
- `npx @cap-js/cds-typer "*" --outputDirectory gen/srv/@cds-models` - will make sure the @cds-models are generated.

5. Run to produce the `.mtar file`
5. Run command to produce the `.mtar file`

```bash
mbt build
Expand Down
38 changes: 37 additions & 1 deletion lib/decorators/method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
type DRAFT_EVENTS,
type CdsFunction,
type CdsEvent,
type ReturnErrorRequest,
} from '../util/types/types';
import { MetadataDispatcher } from '../util/helpers/MetadataDispatcher';
import Constants from '../util/constants/Constants';
Expand Down Expand Up @@ -140,6 +141,33 @@ function buildOnEvent(options: { event: CRUD_EVENTS; handlerType: HandlerType; i
};
}

/**
* Builds a decorator for handling the .on('error) method.
*
* @param event - The custom action event to handle.
* @param handlerType - The type of handler (Before, After, On).
*/

function buildOnError(options: { event: CRUD_EVENTS; handlerType: HandlerType; isDraft: boolean }) {
return function <Target extends Object>() {
return function (
target: Target,
_: string | symbol,
descriptor: TypedPropertyDescriptor<ReturnErrorRequest>,
): void {
const metadataDispatcher = new MetadataDispatcher(target, Constants.DECORATOR.METHOD_ACCUMULATOR_NAME);
const { event, handlerType, isDraft } = options;

metadataDispatcher.addMethodMetadata({
event,
handlerType,
callback: descriptor.value!,
isDraft,
});
};
};
}

/**
* Builds a decorator for handling the .on method.
*
Expand Down Expand Up @@ -406,10 +434,17 @@ const OnFunction = buildOnAction({ event: 'FUNC', handlerType: HandlerType.On, i
/**
*
* This decorator can be applied to methods that need to execute custom logic when a custom function event is triggered.
* @see [CDS-TS-Dispatcher - On function](https://github.com/dxfrontier/cds-ts-dispatcher#onfunction)
* @see [CDS-TS-Dispatcher - On function](https://github.com/dxfrontier/cds-ts-dispatcher#onevent)
*/
const OnEvent = buildOnEvent({ event: 'EVENT', handlerType: HandlerType.On, isDraft: false });

/**
*
* This decorator can be applied to methods that needs to catch the errors.
* @see [CDS-TS-Dispatcher - On function](https://github.com/dxfrontier/cds-ts-dispatcher#onerror)
*/
const OnError = buildOnError({ event: 'ERROR', handlerType: HandlerType.On, isDraft: false });

/**
*
* This decorator can be applied to methods when a new draft is created from an active instance.
Expand Down Expand Up @@ -555,6 +590,7 @@ export {
OnAction,
OnFunction,
OnEvent,
OnError,
OnBoundAction,
OnBoundFunction,
// ON events - Draft
Expand Down
Loading

0 comments on commit 10e75e6

Please sign in to comment.