From 3e7b68882a945ee83d07b0d1321eca4868707360 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Sat, 28 Jan 2017 19:43:36 +0100 Subject: [PATCH] redux middleware --- Rewritten-docs.md | 95 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/Rewritten-docs.md b/Rewritten-docs.md index 6528aa4..3778d0b 100644 --- a/Rewritten-docs.md +++ b/Rewritten-docs.md @@ -1627,7 +1627,7 @@ However due to the lack of time automatic monitoring of the backend state will not be implemented in the early versions of this project but might be implemented in some of the next releases. -### The WebApp +### Web Application The web application ("WebApp") is one of the possible client applications of the ReCodEx system. Creating a web application as the first client application has @@ -1711,15 +1711,17 @@ worth considering: We decided to use React and Redux over Angular 2 for several reasons: -- there is a large community arround these libraries and there is a large number - of tutorials, libraries, and other resources available online -- many of the web frontend developers are familiar with React and Redux and - contributing to the project should be easy for them -- Angular 2 was still not finished at the time we started developing the web - application -- we had previous experience with React and Redux and Angular 2 did not bring +- There is a large community arround these libraries and there is a large number + of tutorials, libraries, and other resources available online. +- Many of the web frontend developers are familiar with React and Redux and + contributing to the project should be easy for them. +- A stable version of Angular 2 was still not released at the time we started + developing the web application. +- We had previous experience with React and Redux and Angular 2 did not bring any significant improvements and features over React so it would not be worth - learning the paradigms of a new framework + learning the paradigms of a new framework. +- It is easy to debug React component tree and Redux state transitions + using extensions for Google Chrome and Firefox. #### User Interface Design @@ -3122,9 +3124,28 @@ object and the action object and it creates a new state. This process is very ea reason about and is also very easy to test using unit tests. Please read the [redux documentation](http://redux.js.org/) for detailed information about the library. +![Redux state handling schema](https://camo.githubusercontent.com/af8803571294fe373a54d039be8f9709f15a2ad4/687474703a2f2f6d616b6569746f70656e2e636f6d2f7374617469632f696d616765732f72656475785f666c6f7763686172742e706e67) + ##### Redux Middleware -@todo +A middleware in redux is a function which can process actions before they are passed +to the reducers to update the state. + +The middleware used by the ReCodEx store is defined in the `src/redux/store.js` script. +Several open source libraries are used: + +- [redux-promise-middleware](https://github.com/pburtchaell/redux-promise-middleware) +- [redux-thunk](https://github.com/gaearon/redux-thunk) +- [react-router-redux](https://github.com/reactjs/react-router-redux) + +We created two other custom middleware functions to simplify the development: + +- **API middleware** -- The middleware filters out all actions with the *type* set to `recodex-api/CALL`, + sends a real HTTP request according to the information in the action. +- **Access Token Middleware** -- This middleware persists the access token each time after the + user signs into the application into the local storage and the cookies. The token + is removed when the user decides to sign out. The middleware also attaches the token + to each `recodex-api/CALL` action when it does not have an access token set explicitly. ##### Accessing The Store Using Selectors @@ -3147,6 +3168,60 @@ Having up-to-date URLs gives the users the possibility to reload the page if som error occurs on the page and land at the same page as he or she would expect. Users can also send links to the very page they want to. +### Creating HTTP Requests + +All of the HTTP requests are made by dispatching a specific action which will be processed +by our custom *API middleware*. The action must have the *type* property set to +`recodex-api/CALL`. The middleware catches the action and it sends a real HTTP request +created according to the information in the `request` property of the action: + +- **type** -- Type prefix of the actions which will be dispatched automatically during the + lifecycle of the request (pending, fulfilled, failed). +- **endpoint** -- The URI to which the request should be sent. All endpoints will be prefixed + with the base URL of the API server. +- **method** (*optional*) -- A string containing the name of the HTTP method which should be used. + The default method is `GET`. +- **query** (*optional*) -- An object containing key-value pairs which will be put + in the URL of the request in the query part of the URL. +- **headers** (*optional*) -- An object containing key-value paris which will be appended + to the headers of the HTTP request. +- **accessToken** (*optional*) -- Explicitly set the access token for the request. The token + will be put in the *Authorization* header. +- **body** (*optional*) -- An object or an array which will be recursively flattened into + the `FormData` structure with correct usage of square brackets for nested (associative) + arrays. It is worth mentioning that the keys must not contain a colon in the string. +- **doNotProcess** (*optional*) -- A boolean value which can disable the default procesing + of the response to the request which includes showing a notification to the user in case + of a failiure of the request. All requests are processed in the way described above + by default. + +The HTTP requests are sent using the `fetch` API which returns a *Promise* of the request. +This promise is put into a new action and creates a new action containing the promise +and the type specified in the `request` description. This action is then caught by the +promise middleware and the promise middleware dispatches actions whenever the state of +the promise changes during its the lifecycle. The new actions have specific types: + +- {$TYPE}_PENDING -- Dispatched immediatelly after the action is processed by the promise + middleware. The `payload` property of the action contains the body of the request. +- {$TYPE}_FAILED -- Dispatched if the promise of the request is rejected. +- {$TYPE}_FULFILLED -- Dispatched when the response to the request is received and the + promise is resolved. The `payload` property of the action contains the body of the + HTTP response parsed as JSON. + +### Routine CRUD Operations + +For routine CRUD (Create, Read, Update, Delete) operations which are common to most +of the resources used in the ReCodEx (e.g., groups, users, assignments, solutions, +solution evaluations, source code files) a set of functions called *Resource manager* +was implemented. It contains a factory which creates basic actions (e.g., `fetchResource`, +`addResource`, `updateResource`, `removeResource`, `fetchMany`) and handlers for +all of the lifecycle actions created by both the API middleware and the promise middleware +which can be used to create a basic reducer. + +The *resource manager* is spread over several files in the `src/redux/helpers/resourceManager` +directory and is covered with unit tests in scripts located at +`test/redux/helpers/resourceManager`. + ### Servier-side Rendering To speed-up the initial time of rendering of the web application a technique called server-side