The goal of CDS-TS-Dispatcher is to significantly reduce the boilerplate code required to implement TS handlers provided by the SAP CAP framework.
- CDS-TS Dispatcher
Install @sap/cds-dk globally:
npm i -g @sap/cds-dk
Use the following steps if you want to create a new SAP CAP project.
- Create new folder :
mkdir new-sap-cap-project
cd new-sap-cap-project
- Initialize the CDS folder structure :
cds init
- Add the the following NPM packages :
npm install @dxfrontier/cds-ts-dispatcher @sap/cds express
npm install --save-dev @types/node @cap-js/sqlite typescript
- Add a tsconfig.json :
tsc --init
- It is recommended to use the following tsconfig.json properties:
{
"compilerOptions": {
/* Base Options: */
"esModuleInterop": true,
"skipLibCheck": true,
"allowJs": true,
"strictPropertyInitialization": false,
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true,
"strictNullChecks": true,
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
/* Allow decorators */
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
/* Strictness */
"strict": true,
"lib": ["es2022"],
"outDir": "./gen/srv"
},
"include": ["./srv"]
}
- Run the
CDS-TS
server
cds-ts watch
Use the following steps if you want to add only the @dxfrontier/cds-ts-dispatcher to an existing project :
npm install @dxfrontier/cds-ts-dispatcher
It is recommended to use the following tsconfig.json properties:
{
"compilerOptions": {
/* Base Options: */
"esModuleInterop": true,
"skipLibCheck": true,
"allowJs": true,
"strictPropertyInitialization": false,
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true,
"strictNullChecks": true,
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
/* Allow decorators */
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
/* Strictness */
"strict": true,
"lib": ["es2022"],
"outDir": "./gen/srv"
},
"include": ["./srv"]
}
Warning
If below message appears
-----------------------------------------------------------------------
WARNING: Package '@sap/cds' was loaded from different installations: [
'***/node_modules/@sap/cds/lib/index.js',
'***/node_modules/@dxfrontier/cds-ts-dispatcher/node_modules/@sap/cds/lib/index.js'
] Rather ensure a single install only to avoid hard-to-resolve errors.
-----------------------------------------------------------------------
Run the following command :
npm install @sap/cds@latest
Execute the following commands :
cds add typer
npm install
Tip
If above option is being used, this means whenever we change a .CDS
file the changes will be reflected in the generated @cds-models
folder.
Execute the command :
npx @cap-js/cds-typer "*" --outputDirectory ./srv/util/types/entities
- Target folder :
./srv/util/types/entities
- Change to your desired destination folder.
Tip
If above option is being used, you have to run every time the command when you do a change in a .CDS file
Caution
Import always the generated entities from the service folders and not from the index.ts
For more info see official SAP CDS-Typer page.
We recommend adhering to the Controller-Service-Repository design pattern using the following folder structure:
- EntityHandler
(Controller)
- Responsible for managing the REST interface to the business logic implemented in ServiceLogic - ServiceLogic
(Service)
- Contains business logic implementations - Repository
(Repository)
- This component is dedicated to handling entity manipulation operations by leveraging the power of CDS-QL.
Controller-Service-Repository
suggested folder structure
CDSDispatcher(entities
: Constructable[]
)
The CDSDispatcher
constructor allows you to create an instance for dispatching and managing entities.
entities (Array)
: An array of Entity handler(s) (Constructable) that represent the entities in the CDS.
initialize
: Theinitialize
method of theCDSDispatcher
class is used to initialize Entity handler(s) and all of their dependencies : Services, Repositories.
Example
import { CDSDispatcher } from '@dxfrontier/cds-ts-dispatcher';
module.exports = new CDSDispatcher([
BookHandler,
ReviewHandler,
UnboundActionsHandler,
// ...
]).initialize();
Visual image
@EntityHandler(entity
: CDSTyperEntity)
The @EntityHandler
decorator is utilized at the class-level
to annotate a class with the specific entity
that will be used in all handlers.
Parameters
entity (CDSTyperEntity)
: A specialized class generated using the CDS-Typer.
Example
import { EntityHandler } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@EntityHandler(MyEntity)
class CustomerHandler {
...
constructor() {}
...
Note
MyEntity was generated using CDS-Typer and imported in the class.
@ServiceLogic()
The @ServiceLogic
decorator is utilized at the class-level
to annotate a class
as a specialized class containing only business logic.
When applying ServiceLogic
decorator, the class becomes eligible to be used with Inject decorator for Dependency injection
Example
import { ServiceLogic } from '@dxfrontier/cds-ts-dispatcher';
@ServiceLogic()
class CustomerService {
...
constructor() {}
...
@Repository()
The @Repository
decorator is utilized as a class-level
annotation that designates a particular class
as a specialized Repository
.
When applying Repository
decorator, the class becomes eligible to be used with Inject decorator for Dependency injection
import { Repository } from '@dxfrontier/cds-ts-dispatcher';
@Repository()
class CustomerRepository {
...
constructor() {}
...
The BaseRepository was designed to reduce the boilerplate code required to implement data access layer for persistance entities.
It simplifies the implementation by offering a set of ready-to-use actions for interacting with the database. These actions include:
.create()
: Create new records in the database..findAll()
: Retrieve all records from the database..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
To get started, refer to the official documentation BaseRepository. Explore the capabilities it offers and enhance your data access layer with ease.
Example
import { Repository } from '@dxfrontier/cds-ts-dispatcher';
import { BaseRepository } from '@dxfrontier/cds-ts-repository';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@Repository()
class CustomerRepository extends BaseRepository<MyEntity> {
constructor() {
super(MyEntity);
}
public async aMethod() {
const created = this.create(...)
const createdMany = this.createMany(...)
const updated = this.update(...)
// ...
}
}
Note
MyEntity was generated using CDS-Typer and imported in the class.
@UnboundActions()
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.
Example
import { UnboundActions } from '@dxfrontier/cds-ts-dispatcher';
@UnboundActions()
class UnboundActionsHandler {
...
constructor() {}
// all unbound actions
...
Imported it in the CDSDispatcher
import { CDSDispatcher } from '@dxfrontier/cds-ts-dispatcher';
module.exports = new CDSDispatcher([UnboundActionsHandler, ...]).initialize();
Note
The reason behind introducing a distinct decorator for Unbound actions
stems from the fact that these actions are not associated with any specific Entity
but instead these actions belongs to the Service itself.
@Inject(serviceIdentifier: ServiceIdentifierOrFunc<unknown>
)
The @Inject
decorator is utilized as a field-level
decorator and allows you to inject dependencies into your classes.
Parameters
serviceIdentifier(ServiceIdentifierOrFunc<unknown>)
: A Class representing the service to inject.
Example
import { EntityHandler, Inject, SRV, Service } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@EntityHandler(MyEntity)
class CustomerHandler {
...
@Inject(CustomerService) private customerService: CustomerService
@Inject(SRV) private srv: Service
...
constructor() {}
...
Note
MyEntity was generated using CDS-Typer and imported in the class.
@Inject(SRV) private srv: Service
This specialized @Inject
can be used as a constant
in @ServiceLogic, @Repository, @EntityHandler and @UnboundActions
classes, it can be accessed trough this.srv
and contains the CDS srv
for further enhancements.
Example
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
...
constructor() {}
...
Note
MyEntity was generated using CDS-Typer and imported in the the class.
Use @BeforeCreate(), @BeforeRead(), @BeforeUpdate(), @BeforeDelete()
to register handlers to run before .on
handlers, frequently used for validating user input.
The handlers receive one argument:
req
of typeTypedRequest
See also the official SAP JS CDS-Before event
Note
If @odata.draft.enabled: true
to manage event handlers for draft version you can use @BeforeCreateDraft(), BeforeReadDraft(), @BeforeUpdateDraft(), @BeforeDeleteDraft()
@BeforeCreate()
It is important to note that decorator @BeforeCreate()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { BeforeCreate, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@BeforeCreate()
public async beforeCreateMethod(req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.before('CREATE', MyEntity, async (req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@BeforeRead()
It is important to note that decorator @BeforeRead()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { BeforeRead, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@BeforeRead()
public async beforeReadMethod(req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.before('READ', MyEntity, async (req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@BeforeUpdate()
It is important to note that decorator @BeforeUpdate()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { BeforeUpdate, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@BeforeUpdate()
public async beforeUpdateMethod(req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.before('UPDATE', MyEntity, async (req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@BeforeDelete()
It is important to note that decorator @BeforeDelete()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { BeforeDelete, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@BeforeDelete()
public async beforeDeleteMethod(req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.before('DELETE', MyEntity, async (req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
Use @AfterCreate(), @AfterRead(), @AfterUpdate(), @AfterDelete()
register handlers to run after the .on
handlers, frequently used to enrich outbound data.
The handlers receive two arguments:
results
(for@AfterRead
): An array of typeMyEntity[]
.result
(for@AfterUpdate
and@AfterCreate
): An object of typeMyEntity
.deleted
(for@AfterDelete
): Aboolean
indicating whether the entity was deleted.req
: An object of typeTypedRequest
.
See also the official SAP JS CDS-After event
Note
If @odata.draft.enabled: true
to manage event handlers for draft version you can use @AfterCreateDraft(), AfterReadDraft(), @AfterUpdateDraft(), @AfterDeleteDraft()
@AfterCreate()
It is important to note that decorator @AfterCreate()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { AfterCreate, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@AfterCreate()
public async afterCreateMethod(results: MyEntity, req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.after('CREATE', MyEntity, async (result, req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@AfterRead()
Example
It is important to note that decorator @AfterRead()
will be triggered based on the EntityHandler argument
MyEntity
import { AfterRead, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@AfterRead()
public async afterReadMethod(results: MyEntity[], req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.after('READ', MyEntity, async (results, req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@AfterUpdate()
It is important to note that decorator @AfterUpdate()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { AfterUpdate, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@AfterUpdate()
public async afterUpdateMethod(result: MyEntity, req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.after('UPDATE', MyEntity, async (result, req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@AfterDelete()
It is important to note that decorator @AfterDelete()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { AfterDelete, Request} from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@AfterDelete()
public async afterDeleteMethod(deleted: boolean, req: Request) {
// ...
}
Equivalent to 'JS'
this.after('DELETE', MyEntity, async (deleted, req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
Use @OnCreate(), @OnRead(), @OnUpdate(), @OnDelete(), OnAction(), @OnFunction(), @OnBoundAction(), @OnBoundFunction()
handlers to fulfill requests, e.g. by reading/writing data from/to databases handlers.
The handlers receive two arguments:
req
of typeTypedRequest
next
of typeFunction
See also the official SAP JS CDS-On event
Note
If @odata.draft.enabled: true
to manage event handlers for draft version you can use @OnCreateDraft(), @OnReadDraft(), @OnUpdateDraft(), @OnDeleteDraft(), @OnBoundActionDraft(), @OnBoundFunctionDraft()
@OnCreate()
It is important to note that decorator @OnCreate()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { OnCreate, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@OnCreate()
public async onCreateMethod(req: TypedRequest<MyEntity>, next: Function) {
// ...
}
Equivalent to 'JS'
this.on('CREATE', MyEntity, async (req, next) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@OnRead()
It is important to note that decorator @OnRead()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { OnRead, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@OnRead()
public async onReadMethod(req: TypedRequest<MyEntity>, next: Function) {
// ...
}
Equivalent to 'JS'
this.on('READ', MyEntity, async (req, next) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@OnUpdate()
It is important to note that decorator @OnUpdate()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { OnUpdate, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@OnUpdate()
public async onUpdateMethod(req: TypedRequest<MyEntity>, next: Function) {
// ...
}
Equivalent to 'JS'
this.on('UPDATE', MyEntity, async (req, next) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@OnDelete()
It is important to note that decorator @OnDelete()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { OnDelete, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@OnDelete()
public async onDeleteMethod(req: TypedRequest<MyEntity>, next: Function) {
// ...
}
Equivalent to 'JS'
this.on('DELETE', MyEntity, async (req, next) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@OnAction(name
: CdsAction)
Parameters
name (CdsAction)
: Representing theCDS action
defined in theCDS file
Example
import { OnAction, ActionRequest, ActionReturn } from "@dxfrontier/cds-ts-dispatcher";
import { AnAction } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@OnAction(AnAction)
public async onActionMethod(req: ActionRequest<typeof AnAction>, next: Function): ActionReturn<typeof AnAction> {
// ...
}
Equivalent to 'JS'
this.on(AnAction, async (req, next) => {
// ...
});
Note
AnAction was generated using CDS-Typer and imported in the the class.
@OnFunction(name
: CdsFunction)
Parameters
name (CdsFunction)
: Representing theCDS action
defined in theCDS file
.
Example
import { OnFunction, ActionRequest, ActionReturn } from "@dxfrontier/cds-ts-dispatcher";
import { AFunction } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@OnFunction(AFunction)
public async onFunctionMethod(req: ActionRequest<typeof AFunction>, next: Function): ActionReturn<typeof AFunction> {
// ...
}
Equivalent to 'JS'
this.on(AFunction, async (req) => {
// ...
});
Note
AFunction was generated using CDS-Typer and imported in the the class.
@OnBoundAction(name
: CdsAction)
It is important to note that decorator @OnBoundAction()
will be triggered based on the EntityHandler argument
=> MyEntity
Parameters
name (CdsAction)
: Representing theCDS action
defined in theCDS file
.
Example
import { OnBoundAction, ActionRequest, ActionReturn } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@OnBoundAction(MyEntity.actions.AnAction)
public async onActionMethod(req: ActionRequest<typeof MyEntity.actions.AnAction>, next: Function): ActionReturn<typeof MyEntity.actions.AnAction> {
// ...
}
Equivalent to 'JS'
this.on(MyEntity.actions.AnAction, MyEntity, async (req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@OnBoundFunction(name
: CdsFunction)
It is important to note that decorator @OnBoundFunction()
will be triggered based on the EntityHandler argument
=> MyEntity
Parameters
name (CdsFunction)
: Representing theCDS action
defined in theCDS file
.
Example
import { OnBoundFunction, ActionRequest, ActionReturn } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@OnBoundFunction(MyEntity.actions.AFunction)
public async onFunctionMethod(req: ActionRequest<typeof MyEntity.actions.AFunction>, next: Function): ActionReturn<typeof MyEntity.actions.AFunction> {
// ...
}
Equivalent to 'JS'
this.on(MyEntity.actions.AFunction, MyEntity, async (req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
Use @BeforeNewDraft(), @BeforeCancelDraft(), @BeforeEditDraft(), @BeforeSaveDraft(), @BeforeCreateDraft(), @BeforeReadDraft(), @BeforeUpdateDraft(), @BeforeDeleteDraft()
to register handlers to run before .on
handlers, frequently used for validating user input.
The handlers receive one argument:
req
of typeTypedRequest
See also the official SAP JS CDS-Before event
@BeforeNewDraft()
Use this decorator when you want to validate inputs before a new draft is created.
It is important to note that decorator @BeforeNewDraft()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { BeforeNewDraft, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@BeforeNewDraft()
public async beforeCreateDraftMethod(req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.before('NEW', MyEntity.drafts, async (req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@BeforeCancelDraft()
Use this decorator when you want to validate inputs before a draft is discarded.
It is important to note that decorator @BeforeCancelDraft()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { BeforeCancelDraft, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@BeforeCancelDraft()
public async beforeCancelDraftMethod(req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.before('CANCEL', MyEntity.drafts, async (req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@BeforeEditDraft()
Use this decorator when you want to validate inputs when a new draft is created from an active instance.
It is important to note that decorator @BeforeEditDraft()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { BeforeEditDraft, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@BeforeEditDraft()
public async beforeEditDraftMethod(req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.before('EDIT', MyEntity, async (req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@BeforeSaveDraft()
Use this decorator when you want to validate inputs when active entity is changed.
It is important to note that decorator @BeforeSaveDraft()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { BeforeSaveDraft, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@BeforeSaveDraft()
public async beforeSaveDraftMethod(req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.before('SAVE', MyEntity, async (req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
Use @AfterNewDraft(), @AfterCancelDraft(), @AfterEditDraft(), @AfterSaveDraft(), @AfterCreateDraft(), @AfterReadDraft(), @AfterUpdateDraft(), @AfterDeleteDraft()
register handlers to run after the .on
handlers, frequently used to enrich outbound data.
The handlers receive two arguments:
The results from the preceding .on
handler, with the following types:
-
results
(of typeMyEntity[]
) for@AfterRead
-
result
(of typeMyEntity
) for@AfterUpdate
and@AfterCreate
-
deleted
(of typeboolean
) for@AfterDelete
-
req
of typeTypedRequest
See also the official SAP JS CDS-After event
@AfterNewDraft()
Use this decorator when you want to enhance outbound data when a new draft is created.
It is important to note that decorator @AfterNewDraft()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { AfterNewDraft, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@AfterNewDraft()
public async afterNewDraftMethod(results: MyEntity, req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.after('NEW', MyEntity.drafts, async (results, req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@AfterCancelDraft()
Use this decorator when you want to enhance outbound data when a draft is discarded.
It is important to note that decorator @AfterCancelDraft()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { AfterCancelDraft, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@AfterCancelDraft()
public async afterCancelDraftMethod(results: MyEntity, req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.after('CANCEL', MyEntity.drafts, async (results, req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@AfterEditDraft()
Use this decorator when you want to enhance outbound data when a new draft is created from an active instance.
It is important to note that decorator @AfterEditDraft()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { AfterEditDraft, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@AfterEditDraft()
public async afterEditDraftMethod(results: MyEntity, req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.after('EDIT', MyEntity, async (results, req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@AfterSaveDraft()
Use this decorator when you want to enhance outbound data when the active entity is changed.
It is important to note that decorator @AfterSaveDraft()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { AfterSaveDraft, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@AfterSaveDraft()
public async afterSaveDraftMethod(results: MyEntity, req: TypedRequest<MyEntity>) {
// ...
}
Equivalent to 'JS'
this.after('SAVE', MyEntity, async (results, req) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
Use @OnNewDraft(), @OnCancelDraft(), @OnCancelDraft(), @OnSaveDraft(), @OnReadDraft(), @OnUpdateDraft(), @OnCreateDraft(), @OnDeleteDraft(), @OnBoundActionDraft(), @OnBoundFunctionDraft()
handlers to support for both, active and draft entities.
The handlers receive two arguments:
req
of typeTypedRequest
next
of typeFunction
See Official SAP Fiori-draft
@OnNewDraft()
This decorator will be triggered when a new draft is created
.
It is important to note that decorator @OnNewDraft()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { OnNewDraft, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@OnNewDraft()
public async onNewDraft(req: TypedRequest<MyEntity>, next: Function) {
// ...
}
Equivalent to 'JS'
this.on('NEW', MyEntity.drafts, async (req, next) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@OnCancelDraft()
This decorator will be triggered when a draft is cancelled
.
It is important to note that decorator @OnCancelDraft()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { OnCancelDraft, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@OnCancelDraft()
public async onCancelDraft(req: TypedRequest<MyEntity>, next: Function) {
// ...
}
Equivalent to 'JS'
this.on('CANCEL', MyEntity.drafts, async (req, next) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@OnEditDraft()
This decorator will be triggered when a new draft is created from an active instance
It is important to note that decorator @OnEditDraft()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { OnEditDraft, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@OnEditDraft()
public async onEditDraft(req: TypedRequest<MyEntity>, next: Function) {
// ...
}
Equivalent to 'JS'
this.on('EDIT', MyEntity, async (req, next) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
@OnSaveDraft()
This decorator will be triggered when the active entity is changed
It is important to note that decorator @OnSaveDraft()
will be triggered based on the EntityHandler argument
=> MyEntity
Example
import { OnSaveDraft, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@OnSaveDraft()
public async onSaveDraft(req: TypedRequest<MyEntity>, next: Function) {
// ...
}
Equivalent to 'JS'
this.on('SAVE', MyEntity, async (req, next) => {
// ...
});
Note
MyEntity was generated using CDS-Typer and imported in the the class.
All active entity On, Before, After events have also a Draft
variant.
Note
Except the @OnAction(), @OnFunction()
as this are bound to the service and not to an entity.
@SingleInstanceCapable()
The @SingleInstanceCapable()
decorator is applied at the method level to indicate that all decorators used in conjunction with this decorator will handle both single instance and entity set requests, this behaves like a switch when the REQUEST is entity set and single instance, so you can manage different behavior.
@SingleInstanceCapable
can be used together with the following decorator actions :
Example 1
: Handling single instance request
- Example single request : http://localhost:4004/odata/v4/main/MyEntity(ID=2f12d711-b09e-4b57-b035-2cbd0a023a09)
import { AfterRead, SingleInstanceCapable, TypedRequest } from "@dxfrontier/cds-ts-dispatcher";
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
@AfterRead()
@SingleInstanceCapable()
public async singeInstanceMethodAndEntitySet(results : MyEntity[], req: TypedRequest<MyEntity>, isSingleInstance: boolean) {
if(isSingleInstance) {
// This will be executed only when single instance read is performed
// isSingleInstance flag will be `true`
return this.customerService.handleSingleInstance(req)
}
}
Example 2
: Differing behavior for single instance and entity set requests
- Example single request : http://localhost:4004/odata/v4/main/
MyEntity(ID=2f12d711-b09e-4b57-b035-2cbd0a023a09)
- Example entity set request : http://localhost:4004/odata/v4/main/
MyEntity
@AfterRead()
@SingleInstanceCapable()
@BeforeRead()
public async singeInstanceMethodAndEntitySet(results : MyEntity[], req: TypedRequest<MyEntity>, isSingleInstance: boolean) {
if(isSingleInstance) {
// This method will be executed for 'AfterRead` single instance
return this.customerService.handleSingleInstance(req)
}
// This method will be executed for `BeforeRead` both cases : single instance & entity set
return this.customerService.handleEntitySet(req)
}
Note
MyEntity was generated using CDS-Typer and imported in the the class.
- Add
mta.yaml
to your project using the following command :
cds add mta
- Install
npm-run-all package
:
npm install --save-dev npm-run-all
- Modify your
package.json
by adding the followingscripts
:
"build:cds": "echo 'STEP 1 : Build CDS' && cds build --production",
"build:ts": "echo 'STEP 2 : Transpile TS => JS' && tsc",
"build:srv:clean:ts": "echo 'Step 3: Clean TS files from srv folder' && find gen/srv/srv -type f -name '*.ts' -delete",
"build:production": "run-s build:cds build:ts build:srv:clean:ts"
- Modify
mta.yaml
as follows :
- builder: custom
commands:
- npm ci
- npm run build:production
- npx @cap-js/cds-typer "*" --outputDirectory gen/srv/@cds-models
npm ci
- Will do a clean installnpm run build:production
- will run the package.json script command for CDS build and transpilation of TS to JSnpx @cap-js/cds-typer "*" --outputDirectory gen/srv/@cds-models
- will make sure the @cds-models are generated.
- Run to produce the
.mtar file
mbt build
- Deploy your
mtar
to BTP
Find here a collection of samples for the CDS-TS-Dispatcher-Samples
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.
Copyright (c) 2023 DXFrontier
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- @dragolea
- @sblessing
- @ABS GmbH team