Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

WFM api framework - HTTP RESTfull api for WFM (ex-webapi) #75

Merged
merged 26 commits into from Aug 14, 2017

Conversation

wtrocki
Copy link
Member

@wtrocki wtrocki commented Aug 8, 2017

Motivation

Allow to use express based API to operate on WFM objects.

Note: this changes are WFM specific and it will require integration with WFM module = sparate PR in angular.js repository

Notes

I have done some investigation for pagination and I did not found anything that will meet all of our criteria. I wrote simple pagination mechanism for this module. We can extract this to separate module if needed somewhere else but I think it makes sense to have it here.

Progress

  • Api definitions
  • Extract to separate module
  • Pagination
  • Security
  • Documentation
  • Unit tests


public list() {
return this.db.collection(this.collectionName).find({});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this class, or an interface definition of it should be async, not all storage libs can fit a synchronous api.

@@ -14,6 +14,7 @@ module.exports = function (grunt) {
},
exec: {
'run': 'ts-node src/index.ts',
'runDebug': 'ts-node --inspect src/index.ts',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@austincunningham We can debug now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need grunt over this same line in an npm script?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I guess it's for reloading the app? There's nodemon for that too

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm.. actually it's for life reload that was supported in previous application..
I couldn't really get that running so life reload will not work now :(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:)

@wtrocki wtrocki changed the title WIP WFM api WFM api framework (ex-webapi) Aug 10, 2017
api.setDb(db);
} else {
console.info('MongoDb not connected', err);
process.exit();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add some non-zero value to make sure the process exits with an error: process.exit(1).

Alternatively this could be done before starting the http server at all:
promisify(MongoClient.connect)(...).then(db => api.setDb(db)).then(() => app.listen(...));

/**
* Default module configuration
*/
export const defaultConfiguration = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could implement ApiConfig, just to inherit jsdoc info

resultApiName?: string;
workorderCollectionName?: string;
workflowCollectionName?: string;
resultCollectionName?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need jsdocs for these keys

const router: express.Router = express.Router();
const workorderController = new ApiController(router, this.workorderService, this.config.workorderApiName);
const workflowController = new ApiController(router, this.workflowService, this.config.workflowApiName);
const resultController = new ApiController(router, this.resultService, this.config.resultApiName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-1 for extra class here, this is using a constructor only for side-effects (mutating router).

Just need something like

router.use(`/${workorderApiName}`, buildRouter(this.workorderService))

so you don't need to mutate router inside another function, and reduces # of params for buildRouter.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 That will be also easy to unit tests. Do you see api exposing each crud method or we should get entire CRUD operations as now?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's okay to return a router with all the methods, the idea behind router.use('/mount-point', middleware) is that that middleware can properly handle everything from that point onwards.

* Interface is being used internally to perform database operations for WFM models
* It's not recomended to use it for other application business logic.
*/
export interface CrudRepository {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like using the term CRUD itself, feels like too much slang and signals boilerplate code.

Could be PagingDataRepository or something, much fancier :D

* Note: pages are counted from number 1.
* Page 0 requests will return empty data.
*/
export class PaginationEngine {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe MongoPaginationEngine? Implementation is mongo-specific.

});
});
}
protected errorHandler(req: express.Request, res: express.Response, error: ApiError) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need a local error handler for this router.
This would prevent the application to define it's own global error handler, and even try to handle errors from this module in a different way.

We do have a global error handler in the app by the way: https://github.com/feedhenry-raincatcher/raincatcher-core/blob/master/demo/server/src/app.ts#L66

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, production error handlers can redirect a user to a 'oops' page, log exceptions to external storage or email, etc.

Copy link
Member Author

@wtrocki wtrocki Aug 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you suggesting here. I wanted this module to be responsible for error handling as app currently tries to render html. This will throw nice json.

What will be your suggestion - throw error here ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, in order to allow the top level app to use the global error handler you add the third param and call next(err), the app might crash on uncaught errors.

The global error handler that I linked should return a formatted json just the same, but it's possible to override it at that global point.

See http://expressjs.com/en/guide/error-handling.html, more specifically:

    PaidContent.find(function (err, doc) {
      if (err) return next(err)
      res.json(doc)
    })

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just do not wanted this module to be self sufficient and do not leak some of the elements to users top level application, but this aproach will be more aligned with express patterns.

@wtrocki wtrocki force-pushed the RAINCATCH-1145 branch 2 times, most recently from d691630 to 8cfaf10 Compare August 10, 2017 21:23
@wtrocki wtrocki changed the title WFM api framework (ex-webapi) WFM api framework - HTTP RESTfull api for WFM (ex-webapi) Aug 11, 2017
@@ -1,7 +1,10 @@
{
"morganOptions": null,
"logStackTraces": true,
"sync":{
"security": {
"apiRole": "ADMIN"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JameelB is that admin or ADMIN ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's admin

@coveralls
Copy link

coveralls commented Aug 13, 2017

Coverage Status

Changes Unknown when pulling 16d8bb0 on wtrocki:RAINCATCH-1145 into ** on feedhenry-raincatcher:master**.


Module used to expose express based api for WFM objects.

### WFM speficic implementations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

specific

## Rest API

Module provides a way to dynamically create API for different business objects.
Created api will use simplied implementations for typical create, read, delete and update operations.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simplified


## Rest API definitions

Definitions apply to every to object exposed by this API. Placeholder `{object}` can be replaced by `workflow`, `workorder` and `result`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove to in every to object


Mongodb running on standard port

### Runining example
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running

@@ -0,0 +1,9 @@

/**
* Interface used to construct standarized response for api error handlers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

standardized

@@ -0,0 +1,9 @@

/**
* Interface used to construct standarized response for api error handlers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

standardized

getLogger().debug('Api delete method called', { params: req.params });

if (!req.params.id) {
const error = new ApiError(errorCodes.MISSING_ID, 'Missing id parameter');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing statusCode 400?

getLogger().debug('Api update method called', { body: req.body });

if (!req.body) {
const error = new ApiError(errorCodes.CLIENT_ERROR, 'Missing request body');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing statusCode 400?

* Interface used to construct standarized response for api error handlers.
*/
export class ApiError extends Error {
constructor(public code: string, public message: string, statusCode: number = 500) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code and statusCode not used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeScript magic. Adding public is exposing them as fields.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may need to add public to statusCode as well?

## Rest API

Module provides a way to dynamically create API for different business objects.
Created api will use simplied implementations for typical create, read, delete and update operations.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly better to say
"create, read, update and delete" - think of CRUD


Module provides a way to dynamically create API for different business objects.
Created api will use simplied implementations for typical create, read, delete and update operations.
It's not recomended to use it outside WFM framework to store user related objects. Please use mongodb driver directly.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. What does "it" refer to in "to use it" - the rest api?
  2. outside "the" WFM framework

}
```

> **Note:** If you apply security middleware additional `401` and `403` statuses may be returned

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: "If you apply middleware security, additional "


curl http://localhost:3000/api/workorders

Note: This example assumes that you have run demo application that populated your database.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...run "the" demo application...

@coveralls
Copy link

coveralls commented Aug 14, 2017

Coverage Status

Changes Unknown when pulling 8efbf0c on wtrocki:RAINCATCH-1145 into ** on feedhenry-raincatcher:master**.

@coveralls
Copy link

Coverage Status

Changes Unknown when pulling 112d2ca on wtrocki:RAINCATCH-1145 into ** on feedhenry-raincatcher:master**.

/** Used to create workflow route */
workflowApiName?: string;
/** Used to create result route */
resultApiName?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can get rid of the Used tos here, and more detail for the client developer: Root path for the Result http endpoints.

Copy link
Member

@austincunningham austincunningham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verified

@coveralls
Copy link

Coverage Status

Changes Unknown when pulling 6388fe3 on wtrocki:RAINCATCH-1145 into ** on feedhenry-raincatcher:master**.

1 similar comment
@coveralls
Copy link

Coverage Status

Changes Unknown when pulling 6388fe3 on wtrocki:RAINCATCH-1145 into ** on feedhenry-raincatcher:master**.

@wtrocki wtrocki merged commit f3029bb into feedhenry-raincatcher:master Aug 14, 2017
@wtrocki wtrocki deleted the RAINCATCH-1145 branch August 14, 2017 14:44
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants