-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
299 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,2 @@ | ||
#!/usr/bin/env sh | ||
. "$(dirname -- "$0")/_/husky.sh" | ||
|
||
npm run lint | ||
npm run test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
The create class, allows you to specify witch fields the user is able to submit, you can set this in the `fields` parameter of the `_meta` variable. | ||
|
||
Methods available: | ||
|
||
- `getSchema();` this allows you to live change your openapi schema | ||
- `async getObject(): Promise<O<typeof this.meta>>;` this allows you to customize the object after validation | ||
- `async before(data: O<typeof this.meta>): Promise<O<typeof this.meta>>;` add custom logic before creating the object | ||
- `async create(data: O<typeof this.meta>): Promise<O<typeof this.meta>>;` create the object in the database | ||
- `async after(data: O<typeof this.meta>): Promise<O<typeof this.meta>>;` add custom logic after creating the object | ||
- `async handle(...args);` overwrite the response or the order of the method calls | ||
|
||
Call order: | ||
|
||
```ts | ||
async function handle(...args) { | ||
let obj = await this.getObject(); | ||
|
||
obj = await this.before(obj); | ||
|
||
obj = await this.create(obj); | ||
|
||
obj = await this.after(obj); | ||
|
||
return { | ||
success: true, | ||
result: this.meta.model.serializer(obj as object), | ||
}; | ||
} | ||
``` | ||
|
||
Example router registration: | ||
```ts | ||
// Primary keys not present in the url are expected in the request body | ||
router.post("/gateways/:gateway_id/evaluations", CreateEvaluation); | ||
``` | ||
|
||
|
||
## D1 Endpoint | ||
The D1 create adapter exposes 3 additional configuration: | ||
|
||
- `dbName`: your D1 binding name, by default is set to "DB" | ||
- `logger`: this allows you to get logger messages for validation errors, objects created and other; | ||
- `constraintsMessages`: this allows you to customize database constraint error messages | ||
|
||
Here is an example of a simple create endpoint, using the example model defined in the [getting started with cruds example](./getting-started-with-cruds.md): | ||
```ts | ||
import { D1CreateEndpoint } from 'chanfana' | ||
|
||
export class CreateEvaluation extends D1CreateEndpoint { | ||
_meta = { | ||
model: evaluationModel, | ||
fields: evaluationModel.schema | ||
.pick({ | ||
gateway_id: true, | ||
name: true, | ||
}) | ||
}; | ||
} | ||
``` | ||
|
||
### Customizing constraints messages | ||
In order to have custom error messages for database constraint error, just define the field inside your class | ||
with an object where the key is a comma separated list of the fields in the constraint and the value is a exception of the type | ||
|
||
```ts | ||
import { D1CreateEndpoint, InputValidationException } from 'chanfana' | ||
|
||
export class CreateEvaluation extends D1CreateEndpoint { | ||
_meta = {...} | ||
|
||
constraintsMessages = { | ||
"evaluations.name, evaluations.gateway_id": new InputValidationException( | ||
"An object with this name already exists", | ||
["body", "name"], | ||
), | ||
}; | ||
} | ||
``` | ||
|
||
## Bring your own Database Endpoint | ||
|
||
In our to use Bring your own Database, just extend your class from the base `CreateEndpoint` and overwrite the `create` method. | ||
|
||
```ts | ||
import { CreateEndpoint } from 'chanfana' | ||
|
||
export class MyCustomCreate extends CreateEndpoint { | ||
async create(data: O<typeof this.meta>): Promise<O<typeof this.meta>> { | ||
// TODO: insert my object here, data is already validated | ||
|
||
// Return the insert object | ||
return data; | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
Methods available: | ||
|
||
- `getSchema();` this allows you to live change your openapi schema | ||
- `async getObject(filters: Filters): Promise<O<typeof this.meta> | null>;` this parses the filters from the query and url parameters | ||
- `async getObject(): Promise<O<typeof this.meta>>;` this receives the filters and returns the object that will be deleted | ||
- `async before(oldObj: O<typeof this.meta>, filters: Filters): Promise<Filters>;` add custom logic before deleting the object, allows you to inject additional sql conditions | ||
- `async after(data: O<typeof this.meta>): Promise<O<typeof this.meta>>;` add custom logic after deleting the object | ||
- `async delete(oldObj: O<typeof this.meta>, filters: Filters): Promise<O<typeof this.meta> | null>;` deletes the object | ||
- `async handle(...args);` overwrite the response or the order of the method calls | ||
|
||
Call order: | ||
|
||
```ts | ||
async function handle(...args) { | ||
let filters = await this.getFilters(); | ||
|
||
const oldObj = await this.getObject(filters); | ||
|
||
if (oldObj === null) { | ||
throw new NotFoundException(); | ||
} | ||
|
||
filters = await this.before(oldObj, filters); | ||
|
||
let obj = await this.delete(oldObj, filters); | ||
|
||
if (obj === null) { | ||
throw new NotFoundException(); | ||
} | ||
|
||
obj = await this.after(obj); | ||
|
||
return { | ||
success: true, | ||
result: this.meta.model.serializer(obj), | ||
}; | ||
} | ||
``` | ||
|
||
Example router registration: | ||
```ts | ||
// Primary keys not present in the url are expected in the request body | ||
router.delete("/gateways/:gateway_id/evaluations/:id", DeleteEvaluation); | ||
``` | ||
|
||
## D1 Endpoint | ||
The D1 delete adapter exposes 2 additional configuration: | ||
|
||
- `dbName`: your D1 binding name, by default is set to "DB" | ||
- `logger`: this allows you to get logger messages for validation errors, objects deleted and other; | ||
|
||
Here is an example of a simple delete endpoint, using the example model defined in the [getting started with cruds example](./getting-started-with-cruds.md): | ||
```ts | ||
import { D1DeleteEndpoint } from 'chanfana' | ||
|
||
export class DeleteEvaluation extends D1DeleteEndpoint { | ||
_meta = { | ||
model: evaluationModel | ||
}; | ||
} | ||
``` | ||
|
||
## Bring your own Database Endpoint | ||
|
||
In our to use Bring your own Database, just extend your class from the base `DeleteEndpoint` and overwrite the `delete` and `getObject` methods. | ||
|
||
```ts | ||
import { DeleteEndpoint } from 'chanfana' | ||
|
||
export class MyCustomDelete extends DeleteEndpoint { | ||
async getObject(filters: Filters): Promise<O<typeof this.meta> | null> { | ||
// TODO: retreive my object using filters | ||
|
||
if (myObject ===undefined) { | ||
// if there is no object matching the filters, return null to throw a http 404 | ||
return null; | ||
} | ||
|
||
// Object found, return it | ||
return myobject | ||
} | ||
|
||
async delete(oldObj: O<typeof this.meta>, filters: Filters): Promise<O<typeof this.meta> | null> { | ||
// the variable oldObj will contain the object from getObject | ||
|
||
// TODO: actually delete the object | ||
|
||
if (result.meta.changes === 0) { | ||
// If there were no sql changes, you can return null to throw a http 404 | ||
return null; | ||
} | ||
|
||
// Return deleted object, this will be included in the endpoint response | ||
return oldObj; | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
chanfana now has built-in classes for the common CRUD operation. | ||
|
||
This allows you to get your work done faster, just create a new class that extends the operation you want and define your model. | ||
|
||
Currently, there is support for `D1 Databases` and `Bring your own database`. | ||
|
||
## Model definition | ||
A chanfana model is compose of the following fields: | ||
|
||
- `tableName`: the target table name | ||
- `schema`: a Zod object containing all your database fields | ||
- `primaryKeys`: an array of the primary keys of your table, this is used for updated, deletes and other operations | ||
- `serializer`: optionally, a serializer function, this is useful when you don't want to show all fields in the response or convert something | ||
- `serializerSchema`: optionally, the Zod schema output of the serializer function defined above | ||
|
||
Start by defining your database model in this format: | ||
```ts | ||
export const Model = { | ||
tableName: string, | ||
schema: AnyZodObject, | ||
primaryKeys: Array<string>, | ||
serializer: (obj: object) => object, // Optional | ||
serializerSchema: AnyZodObject, // Optional | ||
}; | ||
``` | ||
|
||
!!! note | ||
|
||
You will be able to customize what fields are applied to each operation inside each endpoint class | ||
|
||
|
||
Here's an example model | ||
```ts | ||
import { z } from "zod"; | ||
|
||
export function EvaluationSerializer(obj) { | ||
return { | ||
id: obj.id, | ||
gateway_id: obj.gateway_id, | ||
name: obj.name, | ||
processed: Boolean(obj.processed), | ||
total_logs: obj.total_logs, | ||
created_at: obj.created_at, | ||
modified_at: obj.modified_at, | ||
}; | ||
} | ||
|
||
export const evaluation = z.object({ | ||
id: z.string(), | ||
gateway_id: z.string(), | ||
name: z.string(), | ||
created_at: z.string().datetime(), | ||
modified_at: z.string().datetime(), | ||
processed: z.boolean(), | ||
total_logs: z.number(), | ||
}); | ||
|
||
export const evaluationModel = { | ||
schema: evaluation, | ||
primaryKeys: ["id", "gateway_id"], | ||
tableName: "evaluations", | ||
serializer: EvaluationSerializer, | ||
serializerSchema: evaluation | ||
.pick({ | ||
id: true, | ||
gateway_id: true, | ||
name: true, | ||
processed: true, | ||
total_logs: true, | ||
results: true, | ||
created_at: true, | ||
modified_at: true, | ||
}) | ||
}; | ||
``` | ||
|
||
Learn more about each operation here: | ||
|
||
- [create endpoint](./create-endpoint.md) | ||
- [read endpoint](./read-endpoint.md) | ||
- [update endpoint](./update-endpoint.md) | ||
- [delete endpoint](./delete-endpoint.md) | ||
- [list endpoint](./list-endpoint.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.