-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/resource builder #2
Changes from 25 commits
208892d
0d4a72a
4b85dc5
3abd164
4867997
3f43fc8
af54843
fbafb07
74c6dd7
e6b69da
040c4a6
b01ebee
d3154fd
3741ea9
1e57d17
f57fa75
c9ea94d
6df0c7d
096bc51
00c099e
f9950ec
55a9c43
de5883a
8d9e9b3
ac97b95
e913e86
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"presets": [ | ||
"env", | ||
"flow" | ||
], | ||
"plugins": [ | ||
"transform-runtime", | ||
"transform-object-rest-spread" | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Javascript Node CircleCI 2.0 configuration file | ||
# | ||
# Check https://circleci.com/docs/2.0/language-javascript/ for more details | ||
# | ||
version: 2 | ||
jobs: | ||
build: | ||
docker: | ||
# specify the version you desire here | ||
- image: circleci/node:7.10 | ||
|
||
# Specify service dependencies here if necessary | ||
# CircleCI maintains a library of pre-built images | ||
# documented at https://circleci.com/docs/2.0/circleci-images/ | ||
# - image: circleci/mongo:3.4.4 | ||
|
||
working_directory: ~/repo | ||
|
||
steps: | ||
- checkout | ||
|
||
# Download and cache dependencies | ||
- restore_cache: | ||
keys: | ||
- v1-dependencies-{{ checksum "package.json" }} | ||
# fallback to using the latest cache if no exact match is found | ||
- v1-dependencies- | ||
|
||
- run: yarn install | ||
|
||
- save_cache: | ||
paths: | ||
- node_modules | ||
key: v1-dependencies-{{ checksum "package.json" }} | ||
|
||
# run tests! | ||
- run: | ||
name: Jest Suite | ||
command: yarn jest tests --ci --testResultsProcessor="jest-junit" | ||
environment: | ||
JEST_JUNIT_OUTPUT: "reports/junit/js-test-results.xml" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,13 @@ | ||
{ | ||
"extends": "airbnb-base" | ||
"parser": "babel-eslint", | ||
"extends": "airbnb-base", | ||
"env": { | ||
"browser": true, | ||
"jest": true | ||
}, | ||
"rules": { | ||
"no-underscore-dangle": [1, { | ||
"allowAfterThis": true | ||
}] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[ignore] | ||
|
||
[include] | ||
|
||
[libs] | ||
|
||
[lints] | ||
|
||
[options] | ||
|
||
[strict] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
# Jsonapi Orchestrator | ||
|
||
Building better jsonapi-compliant code | ||
|
||
[![BCH compliance](https://bettercodehub.com/edge/badge/MyJobGlasses/jsonapi-orchestrator?branch=master)](https://bettercodehub.com/) | ||
|
||
[![CircleCI](https://circleci.com/gh/MyJobGlasses/jsonapi-orchestrator.svg?style=svg)](https://circleci.com/gh/MyJobGlasses/jsonapi-orchestrator) | ||
|
||
# Why an orchestrator | ||
|
||
Jsonapi Orchestrator aims to help you to deal with various stages of json:api based API interaction with (for now) redux-based libs and frameworks | ||
|
||
Ever wanted to use... | ||
- json:api communication with your APIs | ||
- Redux based storage of resources retrieved from the API | ||
- Side effects with redux-saga posting to your APIs | ||
- Caching expensive requests to alleviate load on your APIs | ||
- Create/update things in one request (Sidepost) | ||
- Be more explicit about what you're doing (association/disassociation VS creation/deletion) | ||
|
||
**Jsonapi Orchestrator is For You 🎁 🎉** | ||
|
||
# Features & Compatibility oj Jsonapi Orchestrator | ||
|
||
Our version Jsonapi Orchestrator is currently made to work with json:api 1.0 | ||
|
||
Apart from the basic Json:api specification, we aim to support interesting extensions (some of which are projected for json:api v1.1) | ||
|
||
- ✅ [The Sideposting Draft](https://github.com/json-api/json-api/pull/1197) | ||
- 🏗 Supporting `method` in PATCH to distinguish creation VS association | ||
- 🏗 Support for temporary IDs or `lid`s (see https://github.com/json-api/json-api/pull/1244) | ||
- 🏗 Support for easy caching of resources | ||
|
||
# HOW TO | ||
|
||
## Building json:api Requests | ||
|
||
A json:api request can be categorized either as a READ request (GET request) or a WRITE request (POST/PATCH/PUT). At this point we're not sure how we want to perform DELETEs | ||
|
||
Building those requests can be difficult because of the format of filters, sorting, and includes, in addition to specifying the endpoint. In addition, if you ever want to handle caching of requests, you would need to be ablte to specify metadata such as data freshness to decide later if you actually want to fire the query or reuse existing data already fetched. | ||
|
||
Our Jsonapi builders will let you store this metadata so it can be reused with cache managers. | ||
|
||
Here are some examples of basic read and writes | ||
|
||
### APIs | ||
|
||
You can manage a list of multiple APIs easily | ||
|
||
```javascript | ||
// my-apis.js | ||
const EMPLOYEE_API_V1 = new API({ | ||
name: 'Employee API v1', url: 'https://employee.example.com/api/v1' | ||
}) | ||
const EMPLOYEE_API_V2 = new API({ | ||
name: 'Employee API v2', url: process.env.EMPLOYEE_API_V1_URL | ||
}) | ||
const METRICS_API_V2 = new API({ | ||
name: 'Metrics API v2', url: 'https://metrics.example.com/api/v2' | ||
}) | ||
|
||
export default { METRICS_API_V2, EMPLOYEE_API_V1, EMPLOYEE_API_V2 } | ||
|
||
// somewhere | ||
import APIs from 'my-apis' | ||
``` | ||
|
||
### Basic READ of single document | ||
|
||
```javascript | ||
employeeReader = new JsonapiResourceReader({ type: 'employee'}) | ||
employeeReader.sideload({ company: { admins: true } }) | ||
employeeReader.filter({ in_vacation: false }) | ||
employeeReader.sort({ next_holiday_at: 'asc' }) | ||
|
||
requestBuilder = new JsonapiRequestBuilder({ | ||
resource: employeeReader, | ||
path: '/employee/profile/:id', | ||
params: { id: employeeId } | ||
api: APIs.EMPLOYEE_API_V1, | ||
}) | ||
|
||
yield put(requestBuilder.asReduxAction()) | ||
``` | ||
|
||
### Basic POST of single document with sideposting | ||
|
||
Let's POST / Create an employee profile linked to an existing user account | ||
|
||
```javascript | ||
|
||
employeeWriter = new JsonapiResourceWriter({ type: 'employee/profile' }) | ||
# Note, since we did not provide any ID the HttpMethod is inferred to be POST | ||
# Had we provided ({ type: 'employee/profile', id: 'cafebabe' }) it would be inferred to be a PATCH | ||
resourceBuilder.setAttributes(...asReduxAction.attributes) | ||
|
||
// Sidepost the user | ||
userBuilder = new JsonapiResourceWriter({ | ||
type: 'user', | ||
id: 'cafebabe', | ||
attributes: currentUser.attributes | ||
}) | ||
employeeWriter.sidepost({ | ||
relationship: 'user', | ||
method: 'update', # other methods include create/associate/disassociate, refer to the sideposting draft | ||
userBuilder | ||
) | ||
|
||
// Sidepost educations | ||
educationBuilders = action.educations.forEach( (e) => { | ||
educationWriter = new JsonapiResourceWriter({ type: 'education', attributes: e.attributes }) | ||
// e.method should return 'create' 'update' or 'destroy' or 'disassociate' | ||
employeeWriter.sidepost({ relationship: 'educations', method: e.method, educationWriter) | ||
}) | ||
|
||
requestBuilder = new JsonapiRequestBuilder({ | ||
builder: employeeWriter, | ||
method: 'POST', | ||
api: APIs.EMPLOYEE_API_V1, | ||
attributes: existingEmployee.attributes | ||
}) | ||
requestBuilder.endpointPath = '/employee/profile' | ||
requestBuilder.addMeta({ invitation_token: invitation_token }) // SHould merge with existing metas | ||
|
||
yield put(requestBuilder.asReduxAction()) | ||
``` | ||
|
||
You can find more advanced examples, including redux-saga based examples, [in the /examples folder](./examples/) | ||
|
||
## Feeding the json:api responses to your Redux state | ||
|
||
TODO by Dimitri | ||
|
||
## Handle caching of requests | ||
|
||
For a later version of JO | ||
|
||
# Credits | ||
|
||
- Dimitri DOMEY (Grand Bidou) @DOMEYD | ||
- Cyril Duchon-Doris (Nickname-yet-to-be-found) @Startouf | ||
|
||
# Contribute | ||
|
||
Well, please | ||
- Open an Issue describing your feature/bug/whatever addition you want to make, | ||
- If you feel 💪 enough, open a PR with some commits and reference your issue number inside. If you're also using ZenHub (💕) you can attach your PR to your issue ! | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing api documentation to explain how to use methods like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. La doc est au niveau de la def des méthodes cf src/builders/JsonapiResourceReader.js There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sinon cf #4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No possibilities to pass an array of string ? (like :
['company.admin', 'profil']
) to be more flexible with sideloading buildingThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.sideload({ company: { admins: true }, profile: true })
marcheThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
['company.admin', 'profil']
c'est une forme qui n'est pas flexible au niveau JS, alors qu'un Object on peut facilement le merge, rajouter des keys, etc.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok