Skip to content

Commit

Permalink
Merge branch 'GPII-4022'
Browse files Browse the repository at this point in the history
* GPII-4022: (27 commits)
  GPII-4022: temporary fix for windows Firefox test timeouts.
  GPII-4022: Brought in newer version of gpii-handlebars with improved directory prioritisation.
  GPII-4022: Updated lingering template directory options arrays.
  GPII-4022: Updated to latest gpii-handlebars to pick up "prioritsable template directory" work.
  GPII-4022: Updated to use latest gpii-handlebars dev release with refactored message resolver.  Updated linting config and fixed errors.
  GPII-4022: Updated to fix failure to render on startup in downstream grades.
  GPII-4022: Removed overly-agressive rerender when validation errors are encountered.
  GPII-4022: Removed wrong-headed "tabindex" from validation errors.
  GPII-4022: Relaxed "events" definition for schema-validated components to allow unresolved IoC references in potentia-ii versions of Infusion.
  GPII-4022: Updated to favour schema as resource instead of adding our own promise handling.  Also fixed browser tests.
  GPII-4022: Finished error binder "resource loading" refactor.
  GPII-4022: Committed broken work in progress on "resource loading" refactor to get input.
  GPII-4022: Added "schema holder" tests to "common" rollup and fix errors following recent refactor.
  GPII-4022: Added the new "schema holder" grade to the top-level index.js.
  GPII-4022: Renamed schema generation promise chaining event to avoid naming confusion with the invoker that triggers the chain.
  GPII-4022: Fixed issues with Headless Chrome Testem launcher.
  GPII-4022: Removed model transformation circuitry from schema holder per feedback from Antranig.
  GPII-4022: Clarify what type of component is expected to be in put to various functions.
  GPII-4022: Removed unused `getSchema` event from schema holder draft.
  GPII-3929: Added "schema holder" base grade and tests.
  ...
  • Loading branch information
amb26 committed Nov 26, 2019
2 parents 8c88f96 + 1393ce9 commit 9bece17
Show file tree
Hide file tree
Showing 36 changed files with 1,329 additions and 759 deletions.
2 changes: 1 addition & 1 deletion .nycrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"reporter": ["html", "text-summary"],
"report-dir": "reports",
"temp-directory": "coverage",
"include": [ "./src/**/*.js", "./index.js"]
"include": [ "src/**/*.js", "index.js"]
}
176 changes: 126 additions & 50 deletions docs/schemaValidationMiddleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ See below for usage examples for both kettle and gpii-express.
The [`errorBinder`](errorBinder.md) component included with this package is designed to associate the validation error
messages produced by the validator with on-screen elements. See that component's documentation for details.

## Components
## Express Components

### `gpii.schema.validationMiddleware.base`
### `gpii.schema.validationMiddleware`

The base grade for both kettle and gpii-express validation middleware. Validates information available in the request
object. The incoming request is first transformed using `fluid.model.transformWithRules`
The base grade for validation middleware used with gpii-express. Supports all the options above, plus the options for
[`gpii.express.middleware`](https://github.com/GPII/gpii-express/blob/master/docs/middleware.md#gpiiexpressmiddleware).
The incoming request is first transformed using `fluid.model.transformWithRules`
and`options.rules.requestContentToValidate`. The results are validated against `options.schemaKey`.

The default options validate the request body, as expected with a `POST` or `PUT` request. See the mix-in grades below
Expand All @@ -43,26 +44,20 @@ The following component configuration options are supported:

| Option | Type | Description |
| -------------------------------- | -------- | ----------- |
| `errorTemplate` | `Object` | If there are validation errors, this object will be merged with the raw error to set the kettle-specific options like `message` and `statusCode`. |
| `inputSchema` | `Object` | The [GSS](gss.md) schema to use in validating incoming request data. |
| `rules.requestContentToValidate` | `Object` | The [rules to use in transforming](http://docs.fluidproject.org/infusion/development/ModelTransformationAPI.html#fluid-model-transformwithrules-source-rules-options-) the incoming data before validation (see below for more details). |

The default `rules.requestContentToValidate` in this grade are intended for use with `PUT` or `POST` body data. This
can be represented as follows:
The default `rules.requestContentToValidate` in the express middleware grade can be represented as follows:

```snippet
requestContentToValidate: {
"": "body"
}
```

These rules extract POST and PUT payloads from the request body created by the [Express body parser
middleware](https://github.com/expressjs/body-parser). See `gpii.schema.validationMiddleware.handlesGetMethod` below
for an example of working with query data.

### `gpii.schema.validationMiddleware`

The base grade for validation middleware used with gpii-express. Supports all the options above, plus the options for
[`gpii.express.middleware`](https://github.com/GPII/gpii-express/blob/master/docs/middleware.md#gpiiexpressmiddleware).
This transformation exposes only the body of the request as a top-level object to be validated. For another example,
see `gpii.schema.validationMiddleware.handlesQueryData` below.

#### Invokers

Expand All @@ -86,26 +81,6 @@ function and let some other downstream piece of middleware continue the conversa

This function is expected to be called by Express (or by an instance of `gpii.express`).

## `gpii.schema.kettle.middleware`

The schema validation [kettle middleware](https://github.com/fluid-project/kettle/blob/master/docs/Middleware.md). Must
be used in combination with a grade that derives from `gpii.schema.kettle.request.http`. See "kettle example" below
for an example of using this grade.

### Component Options

In addition to the options supported by the base grade above, this grade supports the following options:

| Option | Type | Description |
| ---------------- | -------- | ----------- |
| `errorTemplate` | `Object` | If there are validation errors, this object will be merged with the raw error to set the kettle-specific options like `message` and `statusCode`. |

## `gpii.schema.kettle.request.http`

The [request handler](https://github.com/fluid-project/kettle/blob/master/docs/RequestHandlersAndApps.md) portion of
the kettle schema validation middleware. Must be used in combination with a grade that derives from
`gpii.schema.kettle.middleware`. See "kettle example" below for an example of using this grade.

### `gpii.schema.validationMiddleware.handlesQueryData`

A mix-in grade that configures a grade that derives from `gpii.schema.validationMiddleware.base` (so, either the
Expand All @@ -117,7 +92,7 @@ requestContentToValidate: {
}
```

## gpii-express example
### gpii-express example

The `gpii.schema.validationMiddleware` grade is intended to be used with a `gpii.express` or `gpii.express.router`
instance. The `gpii.schema.validationMiddleware.requestAware.router` wrapper is provided as a convenient starting
Expand All @@ -142,23 +117,86 @@ fluid.defaults("gpii.schema.tests.handler", {
});

gpii.express({
gradeNames: ["gpii.schema.validationMiddleware.requestAware.router"],
handlerGrades: ["gpii.schema.tests.handler"],
schemaKey: "valid.json",
schemaDirs: ["%my-package/src/schemas"],
responseSchemaKey: "message.json",
responseSchemaUrl: "http://my.site/schemas/",
path: "/gatekeeper",
port: 3000
gradeNames: ["gpii.schema.validationMiddleware.requestAware.router"],
handlerGrades: ["gpii.schema.tests.handler"],
inputSchema: {
properties: {
key: {
type: "string",
required: true
}
}
},
path: "/gatekeeper",
port: 3000
});
```

If you were to launch this example, you would have a REST endpoint `/gatekeeper` that compares all POST request payloads
to the schema `valid.json`, which can be found in `%my-package/src/schemas`. If a payload is valid according to the
schema, the handler defined above would output a canned "success" message. If the payload is invalid, the underlying
`gpii.express.middleware` instance steps in and responds with a failure message.
to the requestSchema. If a payload is valid according to the schema, the handler defined above would output a canned
"success" message. If the payload is invalid, the underlying `gpii.express.middleware` instance steps in and responds
with a failure message.

## Kettle Components

### `gpii.schema.kettle.validator`

An extension of the `kettle.middleware` grade that is intended to be hosted as a child of your
[`kettle.app`]((https://github.com/fluid-project/kettle/blob/master/docs/RequestHandlersAndApps.md)) grade, and to be
referenced as `requestMiddleware` from one or more of your `kettle.request.http` instances. Each validator validates
a single type of payload. See below for a usage example.

#### Component Options

The following component configuration options are supported:

| Option | Type | Description |
| -------------------------------- | -------- | ----------- |
| `errorTemplate` | `Object` | If there are validation errors, this object will be merged with the raw error to set the kettle-specific options like `message` and `statusCode`. |
| `inputSchema` | `Object` | The [GSS](gss.md) schema to use in validating incoming request data. |
| `rules.requestContentToValidate` | `Object` | The [rules to use in transforming](http://docs.fluidproject.org/infusion/development/ModelTransformationAPI.html#fluid-model-transformwithrules-source-rules-options-) the incoming data before validation (see below for more details). |

The default `rules.requestContentToValidate` in the express middleware grade can be represented as follows:

## kettle example
```snippet
requestContentToValidate: {
"body": "body",
"params": "params",
"query": "query"
}
```

This transformation strips complex internal material from the underlying request and exposes only the parameters (
`params`), query string data (`query`) and request body (`body`). There are convenience grades provided for payloads
where only the query, parameters, or body are validated. See below.

#### Invokers

##### `{gpii.schema.kettle.validator}.handle(requestComponent)`

* `requestComponent`: The `kettle.request.http` instance fielding the actual request.
* Returns: A [`fluid.promise`](https://docs.fluidproject.org/infusion/development/PromisesAPI.html) that will be
resolved if the payload is valid, or rejected with validation errors if the payload is invalid.

This invoker satisfies the basic contract for a `kettle.middleware` grade. It validates a payload and returns a promise
that is resolved if processing should continue or rejected if an invalid payload is detected.

### `gpii.schema.kettle.validator.body`

A convenience validator grade that exposes the request body as a top-level object, so that you can write simpler
schemas to validate your payloads.

### `gpii.schema.kettle.validator.params`

A convenience validator grade that exposes only the URL parameters as a top-level object, so that you can write simpler
schemas to validate your payloads.

### `gpii.schema.kettle.validator.query`

A convenience validator grade that exposes only the query string parameters as a top-level object, so that you can write
simpler schemas to validate your payloads.

### Kettle example

```javascript
var fluid = require("infusion");
Expand All @@ -174,9 +212,8 @@ my.kettle.handler.reportSuccess = function (request) {
request.events.onSuccess.fire({ message: "Payload accepted." });
};

// Looking for body content and validate that against our schema.
fluid.defaults("my.kettle.handler", {
gradeNames: ["gpii.schema.kettle.request.http"],
fluid.defaults("my.kettle.validator", {
gradeNames: ["gpii.schema.kettle.validator.body"],
inputSchema: {
type: "object",
properties: {
Expand All @@ -187,6 +224,16 @@ fluid.defaults("my.kettle.handler", {
enumLabels: ["Good Choice"]
}
}
}
});

// Looking for body content and validate that against our schema.
fluid.defaults("my.kettle.handler", {
gradeNames: ["kettle.request.http"],
requestMiddleware: {
validate: {
middleware: "{my.kettle.validator}"
}
},
invokers: {
handleRequest: {
Expand All @@ -197,6 +244,11 @@ fluid.defaults("my.kettle.handler", {

fluid.defaults("my.kettle.app", {
gradeNames: ["kettle.app"],
components: {
myValidator: {
type: "my.kettle.validator"
}
},
requestHandlers: {
gatedBody: {
type: "my.kettle.handler",
Expand All @@ -213,3 +265,27 @@ If you were to run the above example, you would have a kettle app with a `/gated
payload that does not contain a `hasBodyContent` element that is specifically set to the string `good`. Any payload
that contains that element with the correct value would be passed to the underlying handler stub, and result in a
"payload accepted" message.

Note that we use the `gpii.schema.kettle.validator.body` grade to keep our schema simple. If we were to use the base
`gpii.schema.kettle.validator` grade, our schema might look like:

```json5
{
type: "object",
properties: {
body: {
properties: {
hasBodyContent: {
type: "string",
required: true,
enum: ["good"],
enumLabels: ["Good Choice"]
}
}
}
}
}
```

Each of the convenience grades above allow you to avoid one layer of object-and-property nesting when you are only
dealing with one type of data.
4 changes: 2 additions & 2 deletions docs/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ fluid.defaults("my.validating.component", {
var myValidatingComponent = my.validating.component();

var validationResults = myValidatingComponent.validateInput({});
console.log(JSON.stringify(validationResults, null, 2));
fluid.log(JSON.stringify(validationResults, null, 2));
/*
{
"isValid": false,
Expand All @@ -243,7 +243,7 @@ console.log(JSON.stringify(validationResults, null, 2));
*/

var secondValidationResults = myValidatingComponent.validateInput({ foo: true});
console.log(JSON.stringify(secondValidationResults, null, 2));
fluid.log(JSON.stringify(secondValidationResults, null, 2));
/*
{
isValid: true
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ fluid.module.register("gpii-json-schema", __dirname, require);
// Require all of the server-side components at once.
require("./src/js/common/gss-metaschema");
require("./src/js/common/validator");
require("./src/js/common/validation-errors");
require("./src/js/common/schemaHolder");
require("./src/js/common/schemaValidatedComponent");
require("./src/js/common/schemaValidatedModelComponent");

Expand Down
24 changes: 12 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,27 @@
"author": "Tony Atkins <[email protected]>",
"license": "BSD-3-Clause",
"dependencies": {
"ajv": "6.10.0",
"gpii-binder": "1.0.5",
"ajv": "6.10.2",
"gpii-binder": "1.0.6",
"gpii-express": "1.0.15",
"gpii-handlebars": "1.1.4",
"infusion": "3.0.0-dev.20190507T155813Z.4781871fd.FLUID-6148",
"gpii-handlebars": "2.1.0-dev.20191014T141924Z.45a74ef.GPII-4100",
"infusion": "3.0.0-dev.20191009T141140Z.32c9263b4.FLUID-6148",
"kettle": "1.11.0"
},
"devDependencies": {
"eslint": "6.0.0",
"eslint-config-fluid": "1.3.0",
"eslint": "6.5.1",
"eslint-config-fluid": "1.4.0",
"foundation-sites": "6.4.1",
"gpii-grunt-lint-all": "1.0.5",
"gpii-testem": "2.1.10-dev.20190404T122608Z.b51705e.GPII-3457",
"gpii-testem": "2.1.11-dev.20191003T113129Z.477bcc0.GPII-4156",
"grunt": "1.0.4",
"handlebars": "4.1.2",
"markdown-it": "8.4.2",
"handlebars": "4.4.3",
"markdown-it": "10.0.0",
"mkdirp": "0.5.1",
"node-jqunit": "1.1.8",
"nyc": "12.0.2",
"nyc": "14.1.1",
"request": "2.88.0",
"rimraf": "2.6.3",
"testem": "2.16.0"
"rimraf": "3.0.0",
"testem": "2.17.0"
}
}
Loading

0 comments on commit 9bece17

Please sign in to comment.