diff --git a/.babelrc b/.babelrc index 1c745b6..3c33580 100644 --- a/.babelrc +++ b/.babelrc @@ -1,4 +1,34 @@ { - "presets": ["es2015", "stage-0"], - "plugins": ["transform-runtime", "add-module-exports"] + "presets": [ + "@babel/preset-env" + ], + "plugins": [ + "@babel/plugin-transform-runtime", + "@babel/plugin-syntax-dynamic-import", + "@babel/plugin-syntax-import-meta", + "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-json-strings", + [ + "@babel/plugin-proposal-decorators", + { + "legacy": true + } + ], + "@babel/plugin-proposal-function-sent", + "@babel/plugin-proposal-export-namespace-from", + "@babel/plugin-proposal-numeric-separator", + "@babel/plugin-proposal-throw-expressions", + "@babel/plugin-proposal-export-default-from", + "@babel/plugin-proposal-logical-assignment-operators", + "@babel/plugin-proposal-optional-chaining", + [ + "@babel/plugin-proposal-pipeline-operator", + { + "proposal": "minimal" + } + ], + "@babel/plugin-proposal-nullish-coalescing-operator", + "@babel/plugin-proposal-do-expressions", + "@babel/plugin-proposal-function-bind" + ] } diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..bf780f2 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +coverage/** +node_modules/** +dist/** +lib/** +*.spec.js +src/index.html diff --git a/.eslintrc b/.eslintrc index 7209a7b..377c301 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,11 +2,15 @@ "parser": "babel-eslint", "extends": "airbnb/base", "env": { - "browser": true + "browser": true, + "jest": true }, "globals": { "__API_HOST__": false, - "__API_ENDPOINT__": false + "__API_ENDPOINT__": false, + "describe": false, + "it": false, + "expect": false }, "rules": { "comma-dangle": 0, @@ -14,6 +18,7 @@ "id-length": 0, "react/prop-types": 0, "max-len": 0, - "arrow-body-style": 0 + "arrow-body-style": 0, + "no-prototype-builtins": 0 } } diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml new file mode 100644 index 0000000..b1aa060 --- /dev/null +++ b/.github/workflows/node.js.yml @@ -0,0 +1,30 @@ +# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions + +name: Node.js CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [10.x, 12.x, 14.x] + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: yarn install + - run: yarn lint + - run: yarn build + - run: yarn test diff --git a/.gitignore b/.gitignore index e71a6cc..da229b3 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,9 @@ node_modules # Optional REPL history .node_repl_history +# Vim +*.sw* + lib +.DS_Store +.nvmrc diff --git a/.npmignore b/.npmignore index 8eba6c8..fa0d3e8 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +1,5 @@ src/ +.babelrc +webpack.config.js +.idea/ +test/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..84ea01b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +--- +language: node_js +node_js: + - "4" + - "6" + +cache: yarn + +before_install: + - yarn global add babel-cli + +install: + - yarn install + +before_script: + - yarn lint + +script: + - yarn test diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..04dbd11 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,61 @@ +We 💜 contributions +=================== + +While we love contributions, we also need to ensure that our library is of great quality. Thus we require you to follow some simple guidelines when you're submitting your contributions. + +## Reporting Issues and Asking Questions + +Before opening an issue, please search the [issue tracker](https://github.com/dixieio/redux-json-api/issues) to make sure your issue hasn’t already been reported. + +## Development + +Visit the [issue tracker](https://github.com/dixieio/redux-json-api/issues) to find a list of open issues that need attention. + +Fork, then clone the repo: + +``` +git clone https://github.com/your-username/redux-json-api.git +``` + +### Testing + +To run tests: + +``` +yarn run test +``` + +To continuously watch and run tests, run the following: + +``` +yarn run test:watch +``` + +### Linting +Before submitting a PR check for stylistic errors by linting the project: +``` +yarn run lint +``` + +### Building + +To build run: + +``` +yarn run build +``` + +### Submitting a Pull Request + +For non-trivial changes, please open an issue with a proposal for a new feature or refactoring before starting on the work. We don’t want you to waste your efforts on a pull request that we won’t want to accept. + +On the other hand, sometimes the best way to start a conversation *is* to send a pull request. Use your best judgement! + +1. Open a new issue in the [Issue tracker](https://github.com/dixieio/redux-json-api/issues) +1. Fork the repo +1. Create a new feature branch based off the `master` branch +1. Create breaking test(s) before implementing any fixes or functionality +1. Make your changes +1. Submit a pull request, referencing any issue that it resolves + +Thank you, we 💜 your contributions! diff --git a/README.md b/README.md index 0eefd6b..6f0adec 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,29 @@ -Redux ❤️ JSON API ----------------- +# Redux JSON API +### Make Redux 💜 JSON API -Redux actions, action creators and reducers to make life with [JSON API](http://jsonapi.org)s a breeze. +[![Build Status](https://travis-ci.org/dixieio/redux-json-api.svg?branch=2.0)](https://travis-ci.org/dixieio/redux-json-api) -# Features +This library is intended for use in web applications build on Redux, which consumes data from a [JSON API](http://jsonapi.org/). -- Automatically maintains a state of all loaded entities -- Provides actions for - - Creating entity - - Reading endpoint - - Updating entity - - Deleting entity -- Automatically keeps reverse relationships up to date upon creation, update and delete - - This applies only to relationships where the name of the foreign key is directly adhered from entity type—but can be both plural or singular +Use _redux-json-api_ to have one simple way of storing resource objects in Redux state along with it's CRUD API, which provides easy ways to create, read, update and delete resources. -# Usage +Please raise any questions as an [Issue](https://github.com/dixieio/redux-json-api/issues) or submit your contributions as a [Pull Request](https://github.com/dixieio/redux-json-api/pulls). Remember to review our [contributions guidelines](CONTRIBUTING.md). -1. `npm install redux-json-api` -1. Enable JSON API reducer (examples assume that you've connected it to `state.api`) -1. Set up API hostname and path using the actions `setEndpointHost` and `setEndpointPath` -1. Configure access token with `setAccessToken` +# Table of contents +1. [Set-Up & Configure](docs/set-up-configure.md) +1. [API](docs/api.md) +1. [Selectors](docs/selectors.md) +1. [Good reads](#good-reads) +1. [Contribute](#contribute) +1. [Contributors](#contributors) -You're now good to go. So have a look the the available actions below. +## Good reads +- [__Working Example App__](https://github.com/dvidsilva/redux-json-api-demo/) - Quickstart guide to a minimal implementation of Redux JSON API 2 +- [__Redux__](http://redux.js.org/) - Read about redux and core principles. +- [__JSON API__](http://jsonapi.org/) - Read about the specifications for JSON API. -# State structure +## Contribute +Got any feedback or suggestions? Review our [contribution guidelines](CONTRIBUTING.md). -```json -{ - "api": { - "users": { - "data": [ - { - "type": "users", - "id": 1, - "attributes": { } - } - ], - "isInvalidating": "IS_DELETING" - }, - "isCreating": 0, - "isReading": 0, - "isUpdating": 0, - "isDeleting": 0, - "endpoint": { - "host": null, - "path": null, - "accessToken": null - } - } -} -``` - -Entity objects are 1-to-1 with the API response. - -# API - -## Options - -Each function accepts an options object with two callbacks: `onSuccess`, `onError`. - -## Create entity - -`createEntity(entity, options)` - -Pass a full resource object to `createEntity`. It will trigger a request to `POST /${entity.type}`. - -Expects the API to return `200 OK` with the newly created resource object in response body. - -## Read endpoint - -`readEndpoint(endpoint, options)` - -Provide the endpoint from where to read. E.g. `users/1/roles`. The module will read each entity and map them from their type specified. - -## Update entity - -`updateEntity(entity, options)` - -Pass your resource object to `updateEntity`. It will trigger a request to `PATCH /${entity.type}/${entity.id}`. - -Expects the API to return `200 OK` with the updated resource object in response body. - -## Delete entity - -`deleteEntity(entity.options)` - -Accept a resource object and triggers a request to `DELETE /${entity.type}/${entity.id}`. - -Expects the API to return `204 No content`. - -* * * - -Made with ❤️ by Team [Dixie][dixie] - - [dixie]: http://dixie.io +## Contributors +Made with 💜 from Copenhagen and [the world](https://github.com/dixieio/redux-json-api/graphs/contributors). Originally forged in a [Founders](https://www.founders.as) startup. diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..cec825a --- /dev/null +++ b/docs/api.md @@ -0,0 +1,79 @@ +API +--- + +_redux-json-api_ provides a simple API for all four CRUD actions. + +- Create resource object using [createResource](#createresource-resource-object--promise) +- Read endpoints through [readEndpoint](#readendpoint-endpoint-string--promise) +- Update resource with [updateResource](#updateresource-resource-object--promise) +- Delete resource using [deleteResource](#deleteresource-resource-object--promise) +- Add a resource to the store using [hydrateStore](#hydratestore-resource-object--action) +- Manage relationships using [relationship endpoints](api/relationships.md) + +## Resource objects + +Whenever there's referred to a resource object or entity, it must conform to [JSON API specifications](http://jsonapi.org/format/#document-resource-objects). + +## Resource endpoints + +_redux-json-api_ resolves resource objects to endpoints based on their specified type. The following resource will update and delete to _/tasks/1_: + +```json +{ + "id": 1, + "type": "tasks", + "attributes": { + "title": "Lorem ipsum" + } +} +``` + +While dispatching a create action for the following resource will make a request to _/tasks_: + +```json +{ + "type": "tasks", + "attributes": { + "title": "Lorem ipsum" + } +} +``` + + +## API Promises +The _redux-json-api_'s CRUD API methods will all return a single promise. The fulfillment handler will receive one argument with the response body. One exception to this is the fulfillment handler from a `deleteResource` promise, which will not receive any arguments. + + +## API Methods + +Note: Return values noted below are after dispatch, i.e. `dispatch(createResource({ ... }))`. + +#### `createResource( resource: JsonApiResource ): Promise` + +Use this action creator to trigger a POST request to your API with the given resource. + +[Examples and details here.](apis/createResource.md) + +#### `readEndpoint( endpoint: string ): Promise` + +This action creator will trigger a GET request to the specified endpoint. + +[Read more.](apis/readEndpoint.md) + +#### `updateResource( resource: JsonApiResource ): Promise` + +Update entities using this action creator. It will make a PATCH request to your API. + +[Details and examples.](apis/updateResource.md) + +#### `deleteResource( resource: JsonApiResource ): Promise` + +Use this action creator to issue a DELETE request to your API. + +[More details on _deleteResource_](apis/deleteResource.md) + +#### `hydrateStore( payload: JsonApiDocument ): Action` + +Use this action to hydrate the store with e.g. bootstrapped data. + +[More details on _hydrateStore_](apis/hydrateStore.md) diff --git a/docs/apis/createResource.md b/docs/apis/createResource.md new file mode 100644 index 0000000..6d7639c --- /dev/null +++ b/docs/apis/createResource.md @@ -0,0 +1,73 @@ +### `createResource( resource: JsonApiResource ): Promise` + +Dispatch function returned from `createResource` to issue a `POST` request to your API. + +### Endpoint + +The endpoint to which the request is issued is resolved from `type` off the resource object in the request body. + +This resource object will resolve to "/tasks": + +```json +{ + "type": "tasks", + "attributes": { + "task": "New task name" + } +} +``` + +### Example + +```js +import { connect } from 'react-redux'; +import { createResource } from 'redux-json-api'; + +class CreateTask extends Component { + handleSubmit() { + const { dispatch } = this.props; + const entity = { + type: 'tasks', + attributes: { + task: 'New task name' + }, + relationships: { + taskList: { + data: { + id: '1', + type: 'taskLists' + } + } + } + } + + dispatch(createResource(entity)); + } + + render() { + return