kurierjs / kurier Goto Github PK
View Code? Open in Web Editor NEWTypeScript framework to create JSON:API compliant APIs
Home Page: https://kurier.readthedocs.io/en/latest/
License: MIT License
TypeScript framework to create JSON:API compliant APIs
Home Page: https://kurier.readthedocs.io/en/latest/
License: MIT License
[GET]comments/5
{
"data": [
{
"id": 5,
"type": "comments",
"attributes": {}
}
]
}
Expected
{
"data": {
"id": 5,
"type": "comments",
"attributes": {}
}
}
When show => { "data": {} }
When index => { "data": [{}, {}] }
We should be able to configure that per resource. For example, on one of our projects it's called Id
Missing reference: it should say user.attributes[attribute]
, not user[attribute]
.
In both examples of the custom OperationProcessor there's a type error:
https://github.com/ebryn/jsonapi-ts/tree/joel/documentation#how-does-an-operation-gets-executed
and in the Controlling Errors example:
https://github.com/ebryn/jsonapi-ts/tree/joel/documentation#controlling-errors-while-executing-an-operation
Originally posted by @spersico in #67 (comment)
We should expose an endpoint that allows OpenAPI explorers such as https://inspector.swagger.io/builder?url=https%3A%2F%2Fswapi.co%2Fapi%2Fpeople work out of the box with JSONAPI-TS
For now Application
is a singleton which can cause an issue eg. with static current user
The current implementation of filtersToKnex
apparently converts null
to 0
, which isn't accurate. This prevents to lookup records by absent values.
Object.keys(filters).forEach(key => {
let value = filters[key];
const operator = getOperator(filters[key]) || "=";
if (value.substring(value.indexOf(":") + 1)) {
value = value.substring(value.indexOf(":") + 1);
}
value = value !== "null" ? value : 0;
processedFilters.push({
value,
operator,
column: camelize(key)
});
TS 3.1.2 is having trouble to infer the proper signature of .includes()
when the variable can be either a string or a string[]. Updating to the latest TS version solves the problem.
Use cases:
[POST]
{
"data": {
"type": "comments",
"attributes": {
"body": "New message"
},
"relationships": {
"user": {
"data": { "type": "user", "id": "1" }
}
}
}
}
Ignores the user relation completely, currently the work around is to add the userId: 1
to the attributes hash
[POST]
{
"data": {
"type": "comments",
"attributes": {
"body": "New message",
"userId": "1"
},
"relationships": {
"user": {
"data": { "type": "user", "id": "1" }
}
}
}
}
https://github.com/ebryn/jsonapi-ts/tree/joel/documentation#extending-the-operationprocessor-class
and
Originally posted by @spersico in #67 (comment)
When defining compound type names like supplyCategory
, the OperationProcessor.resourceFor() function fails to return the correct type.
Expected: SupplyCategory
Received: Supplycategory
Putting some logs revealed this:
2019-02-18T14:13:28.597530346Z checking if SupplyCategory matches Supplycategory
The following error was thrown after:
2019-02-18T14:13:28.598537676Z JSONAPI-TS: TypeError: resourceClass is not a constructor
2019-02-18T14:13:28.598555998Z at records.map.record (/opt/app/node_modules/@ebryn/jsonapi-ts/dist/processors/knex-processor.js:54:20)
2019-02-18T14:13:28.598561872Z at Array.map (<anonymous>)
2019-02-18T14:13:28.598566464Z at KnexProcessor.convertToResources (/opt/app/node_modules/@ebryn/jsonapi-ts/dist/processors/knex-processor.js:49:24)
2019-02-18T14:13:28.598574428Z at KnexProcessor.get (/opt/app/node_modules/@ebryn/jsonapi-ts/dist/processors/knex-processor.js:19:21)
2019-02-18T14:13:28.598578273Z at <anonymous>
Instead they are being computed assuming Id
-suffixes here: https://github.com/ebryn/jsonapi-ts/blob/dfc30e3922ae9808eecee511667bcdc2ab61d82c/src/processors/knex-processor.ts#L66
Currently if an operation fails because of a database constraint or what ever, I can't handle them gracefully
async add(op: Operation): Promise<any> {
const { password, ...user } = op.data.attributes;
if (!password) {
throw {
// At the very least, you must declare a status and a code.
status: HttpStatusCode.BadRequest,
title: "A password must be entered.",
code: "no_password"
} as JsonApiError;
}
user.salt = uuid()
user.hashedPassword = hash(password, user.salt);
op.data.attributes = user;
try {
return super.add({ ...op, params: {} });
} catch (e) {
throw {
status: HttpStatusCode.BadRequest,
title: "the email already exists",
code: "duplicated_user"
} as JsonApiError;
}
}
This is what I would like/expect, but I don't know if its the best approach.
@rtablada has added some basic infrastructure for tests.
Let's write tests that exercise CRUD operations via HTTP & the bulk operation endpoint
Essentially we'll make HTTP requests and assert their responses look as expected
We should build a simple Mirage emulation layer that allows existing Mirage users to easily swap in jsonapi-ts.
For example, inside your mirage config you can define API handlers like this:
this.get('/authors', function(schema, request) {
});
Which could be serviced by a AuthorsProcessor and the callback could be a shorthand for overriding the get
method on it.
In the example:
https://github.com/ebryn/jsonapi-ts/tree/joel/documentation#extending-the-knexprocessor-class
The property relationships is missing.
Originally posted by @spersico in #67 (comment)
Reflexive relations 0 .. 1 between the same entity are not currently supported.
Example schema
export default class Comment extends Resource {
static get type() {
return 'comment';
}
static schema = {
attributes: {
body: String,
created_at: String,
updated_at: String,
parentCommentId: String
},
relationships: {
user: {
type: () => User,
belongsTo: true,
foreignKeyName: "userId"
},
parentComment: {
type: () => Comment,
belongsTo: true,
foreignKeyName: "parentCommentId"
},
thread: {
type: () => Thread,
belongsTo: true,
foreignKeyName: "threadId"
}
}
};
}
It errors out when I do this query: /comments?include=parentComment
with this error output:
JSONAPI-TS: { error: table name "comments" specified more than once
A common use case is to check if a user can execute an action because they have a certain role.
Roles are usually based in some sort of privilege escalation, let's suppose we have two roles interacting with resources Book
, Author
and Invoice
.
Book
, Author
Book
, Author
(because he exceeds member
) and Invoice
While a better way to express this could be with numeric comparison, it's no as readable.
Currently supported behavior works using .every()
, so the logical chain is bound by AND
.
The target syntax is:
@Authorize(IfUser("role", ["member", "admin"]))
If one of the given values matches, the condition passes.
JSONAPI-TS: TypeError: Cannot read property 'schema' of undefined at ResourceProcessor.get (/c/dev/jsonapi-ts/src/processors/knex-processor.ts:73:47)
at ResourceProcessor.execute (/c/dev/jsonapi-ts/src/processors/operation-processor.ts:20:60) at Application.executeOperation (/c/dev/jsonapi-ts/src/application.ts:29:40)
at process._tickCallback (internal/process/next_tick.js:68:7)
Weird thing about this bug is that apparently doesn't break functionality, but it floods the console for every request I do, using the dummy app.
@spersico In another PR we should set a function to build ${relName}Id
, so can have in a single place the builder for that string.
Originally posted by @joelalejandro in #95
@Authorize
is passing through without checking if the condition provided by IfUser()
is satisfied. Apparently the decorate()
function isn't passing the arguments correctly.
https://knexjs.org
it will basically need to implement get
/add
/update
/remove
methods which do the appropriate SQL things with Knex
Since we're using POJOs to represent errors in JSONAPI-TS, Node is throwing the following warning every time a JsonApiError is thrown in an async function:
(node:1626) Warning: a promise was rejected with a non-error: [object Object]
We should consider refactoring the error system to work with actual Error objects that can be converted into JsonApiError-like POJOs.
Expands upon #81 .
Included Resources aren't being return as valid JSONAPI resources, just extracted into the included property of the response.
For example, the response to a GET /comments/3?include=parentComment
is:
The function responsible for this is:
https://github.com/ebryn/jsonapi-ts/blob/d352cef3e19294d8da2ce329b02db7773d7fd627/src/application.ts#L180
We can port our Koa middleware to use Express pretty easily
Seen on chameleon-api, when a table (let's say comments
) has a column named type, when making a request that includes said table (users?include=comments
).
The included comments don't have the field type.
The code responsible is this function:
https://github.com/ebryn/jsonapi-ts/blob/4a907b9086ae766245cdf5003f0f13149838b21e/src/application.ts#L133
I've edited this issue's description to include my analysis -- @joelalejandro
In order to implement /bulk
operations, a new key { operations: Operation[] }
must be defined in JsonApiDocument.
operations
cannot co-exist with data
or errors
.
An error should be triggered if this condition isn't met.
A bulk operation must be treated as a transaction:
T = { O1, O2, ..., On } => R
If any given Ox fails, all of T must be reverted:
T = O1 ==> O2 ==> O3 =/=> O4 = R
|___________________________|
Rollbacks are relatively trivial for processors derived of a KnexProcessor, where the database engine takes care of the transaction with little concern from JSONAPI-TS.
However, for more generic processors, since we do not know what an operation could entail, we can't automatically figure out how to revert it.
We define RB(O)
as a function around an operation O
that can revert whatever impact (if any) ocurred from executing O
.
RB(O)
should only be necessary for add, update and remove, since get-ops should not have any side-effects.
At a semantic level, rollbacks would act as follows:
S
is a list of states for a given resourceThis could be auto-mapped and executed, but if an opertion has more complex side effects, the user should be able to provide definitions of how a rollback should proceed on such operations:
A BulkOperationProcessor
class should imply the following responsabilities:
(1) is currently handled by the Application
object. Maybe it's something that the BulkOperationProcessor has access to?
// Access current transaction
app.transaction.push(...);
This would require the existence of a Transaction
class.
Currently, a Transaction
is created by the app.createTransaction()
function, which simply wraps the execution of operations in a Promise.all
call.
A public API for a transaction must include:
It should also hold a state array, defined as:
type State = {
operation: Operation;
success: boolean;
error?: JsonApiError;
result?: OperationResponse
}
Chameleon users should be able to login and make edits to resources.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.