Skip to content
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

Personal data query parameter validation #353

Open
wants to merge 3 commits into
base: develop_debrecated
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "maas-schemas",
"version": "5.10.0",
"version": "5.11.0",
"description": "Schemas for MaaS infrastructure",
"main": "index.js",
"engine": {
Expand Down
75 changes: 75 additions & 0 deletions schemas/core/components/personal-data-query.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"$id": "http://maasglobal.com/core/components/personal-data-query.json",
"description": "MaaS personal data query parameters schema",
"definitions": {
"queryParams": {
"type": "object",
"properties": {
"customer[identityId]": {
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 fact that GET query is flat should not affect JSON representation that we validate.

We had similar situation in ontrack and over there @hieuunguyeen used qs to unserialize nested object that came with GET before validation.

That we way we may reuse validation for same nested structures across GET and POST requests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Otrack querystring validation schema is here https://github.com/maasglobal/maas-schemas/blob/develop/schemas/tsp/journey-planner/request.json#L5-L8

Not really performing proper validation

Copy link
Contributor

Choose a reason for hiding this comment

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

I just remember discussing with @hieuunguyeen nesting properties, but didn't look into outcome.

Anyway I think we should just reuse same structures across GET and POST requests, and configure schema only for normal object structures not influenced by limitation of a transport method.

Copy link
Contributor Author

@alapto alapto Jan 11, 2019

Choose a reason for hiding this comment

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

Not dismissing your idea, it could work nicely but requires more effort and in that case I would propose postponing this PR and first merging the TSP changes that we need to get for next release and then apply validation on release after that

Copy link
Contributor

Choose a reason for hiding this comment

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

I also think that schemas should be validating the raw request coming in, regardless of transportation method.
Defining schema with normal object structure hinting that request is required to go through parser before it could be validated.

Copy link
Contributor

@medikoo medikoo Jan 22, 2019

Choose a reason for hiding this comment

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

I also think that schemas should be validating the raw request coming in,

@hieuunguyeen if we follow this advice, then schema's should validate query strings passed in direct form (so e.g. string "foo=bar&other=etc" should be passed to schema validate).

JSON schemas are purely about JSON objects, and to comply to that, we already do not validate raw request, but parse query string format into JSON before validation. I don't think it should be problematic to improve serialization/deserialization method (we already apply on our ends) so it can also handle nested object structures.

Big downside of what's proposed here is that it forces us to maintain two schemas for exactly same data (once handled in format as { customer: { lastName, firstName } }, and then as { "customer[firstName]" , "customer[lastName]" }.

Design and maintenance wise it looks quite bad to me.

"$ref": "http://maasglobal.com/core/components/units.json#/definitions/identityId"
},
"customer[firstName]": {
"description": "First name of the customer (e.g. John)",
"$ref": "http://maasglobal.com/core/components/common.json#/definitions/personalName"
},
"customer[lastName]": {
"description": "Last name of the customer (e.g. Doe)",
"$ref": "http://maasglobal.com/core/components/common.json#/definitions/personalName"
},
"customer[phone]": {
"description": "ITU-T E.164 phone number",
"$ref": "http://maasglobal.com/core/components/common.json#/definitions/phone"
},
"customer[email]": {
"description": "Rough validation of a valid e-mail address",
"$ref": "http://maasglobal.com/core/components/common.json#/definitions/email"
},
"email": {
"description": "Rough validation of a valid e-mail address",
"$ref": "http://maasglobal.com/core/components/common.json#/definitions/email"
},
"customer[address]": {
"$ref": "http://maasglobal.com/core/components/address.json#/definitions/address"
},
"customer[city]": {
"$ref": "http://maasglobal.com/core/components/address.json#/definitions/city"
},
"customer[country]": {
"$ref": "http://maasglobal.com/core/components/address.json#/definitions/country"
},
"customer[zipCode]": {
"$ref": "http://maasglobal.com/core/components/address.json#/definitions/zipCode"
},
"customer[locale]": {
"$ref": "http://maasglobal.com/core/components/i18n.json#/definitions/locale"
},
"customer[appInstanceId]": {
"description": "An id specific to a user device",
"$ref": "http://maasglobal.com/core/components/common.json#/definitions/appInstanceId"
},
"customer[opaqueId]": {
"description": "Typically the hash of the identityId",
"$ref": "http://maasglobal.com/core/components/common.json#/definitions/opaqueId"
},
"customer[clientId]": {
"description": "An id indicating the source of the client",
"$ref": "http://maasglobal.com/core/components/common.json#/definitions/clientId"
},
"customer[dob]": {
"description": "The customer's date of birth or boolean indicating if the value is already in DB",
"$ref": "http://maasglobal.com/core/components/units.json#/definitions/isoDate"
},
"customer[ssid]": {
"description": "Social Security ID",
"$ref": "http://maasglobal.com/core/components/common.json#/definitions/ssid"
},
"customer[subscriberType]": {
"description": "Can indicate if subscriber is using paid or free plan",
"type": "string",
"minLength": 3,
"maxLength": 255
}
}
}
}
}
189 changes: 102 additions & 87 deletions schemas/tsp/booking-options-list/request.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,102 +2,117 @@
"$id": "http://maasglobal.com/tsp/bookings-options-list/request.json",
"description": "Request schema for getting options from a TSP adapter.",
"type": "object",
"properties": {
"mode": {
"$ref": "http://maasglobal.com/core/components/travel-mode.json"
},
"startTime": {
"$ref": "http://maasglobal.com/core/components/units.json#/definitions/time"
},
"endTime": {
"$ref": "http://maasglobal.com/core/components/units.json#/definitions/time"
},
"from": {
"$ref": "http://maasglobal.com/core/components/units-geo.json#/definitions/shortLocationString"
},
"fromName": {
"anyOf": [
{
"$ref": "http://maasglobal.com/core/components/address.json#/definitions/placeName"
"allOf": [
{
"type": "object",
"properties": {
"mode": {
"$ref": "http://maasglobal.com/core/components/travel-mode.json"
},
{
"type": "string",
"maxLength": 0
}
]
},
"fromAddress": {
"anyOf": [
{
"description": "Componentized from address",
"$ref": "http://maasglobal.com/core/components/address.json#/definitions/componentAddress"
"startTime": {
"$ref": "http://maasglobal.com/core/components/units.json#/definitions/time"
},
{
"type": "string",
"maxLength": 0
}
]
},
"fromRadius": {
"description": "'from' location radius in meters",
"$ref": "http://maasglobal.com/core/components/units-geo.json#/definitions/distance"
},
"to": {
"anyOf": [
{
"endTime": {
"$ref": "http://maasglobal.com/core/components/units.json#/definitions/time"
},
"from": {
"$ref": "http://maasglobal.com/core/components/units-geo.json#/definitions/shortLocationString"
},
{
"type": "string",
"maxLength": 0
}
]
},
"toName": {
"anyOf": [
{
"$ref": "http://maasglobal.com/core/components/address.json#/definitions/placeName"
"fromName": {
"anyOf": [
{
"$ref": "http://maasglobal.com/core/components/address.json#/definitions/placeName"
},
{
"type": "string",
"maxLength": 0
}
]
},
{
"fromAddress": {
"anyOf": [
{
"description": "Componentized from address",
"$ref": "http://maasglobal.com/core/components/address.json#/definitions/componentAddress"
},
{
"type": "string",
"maxLength": 0
}
]
},
"fromRadius": {
"description": "'from' location radius in meters",
"$ref": "http://maasglobal.com/core/components/units-geo.json#/definitions/distance"
},
"to": {
"anyOf": [
{
"$ref": "http://maasglobal.com/core/components/units-geo.json#/definitions/shortLocationString"
},
{
"type": "string",
"maxLength": 0
}
]
},
"toName": {
"anyOf": [
{
"$ref": "http://maasglobal.com/core/components/address.json#/definitions/placeName"
},
{
"type": "string",
"maxLength": 0
}
]
},
"toAddress": {
"anyOf": [
{
"$ref": "http://maasglobal.com/core/components/address.json#/definitions/componentAddress"
},
{
"type": "string",
"maxLength": 0
}
]
},
"toRadius": {
"description": "'to' location radius in meters",
"$ref": "http://maasglobal.com/core/components/units-geo.json#/definitions/distance"
},
"distance": {
"$ref": "http://maasglobal.com/core/components/units-geo.json#/definitions/distance"
},
"extraOptions": {
"description": "An arbitrary string passed on a per-TSP basis, e.g. user's subscription period",
"type": "string",
"maxLength": 0
}
]
},
"toAddress": {
"anyOf": [
{
"$ref": "http://maasglobal.com/core/components/address.json#/definitions/componentAddress"
"minLength": 0
},
{
"tspProductIds": {
"description": "Comma-separated list of tspProductIds that the user has access to",
"type": "string",
"maxLength": 0
"minLength": 0
},
"patternProperties": {
"^(optionalParameters).+": {
"type": [
"string",
"number",
"boolean"
]
}
}
]
},
"toRadius": {
"description": "'to' location radius in meters",
"$ref": "http://maasglobal.com/core/components/units-geo.json#/definitions/distance"
},
"distance": {
"$ref": "http://maasglobal.com/core/components/units-geo.json#/definitions/distance"
},
"extraOptions": {
"description": "An arbitrary string passed on a per-TSP basis, e.g. user's subscription period",
"type": "string",
"minLength": 0
},
"tspProductIds": {
"description": "Comma-separated list of tspProductIds that the user has access to",
"type": "string",
"minLength": 0
},
"required": [
"startTime",
"from"
],
"additionalProperties": true
},
"patternProperties": {
"^(optionalParameters).+": {
"type": ["string", "number", "boolean"]
}
{
"$ref": "http://maasglobal.com/core/components/personal-data-query.json#/definitions/queryParams"
}
},
"required": ["startTime", "from"],
"additionalProperties": true
]
}
21 changes: 15 additions & 6 deletions schemas/tsp/booking-read-by-id/request.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@
"$id": "http://maasglobal.com/tsp/bookings-read-by-id/request.json",
"description": "Request schema for getting a specific booking with a TSP ID from a TSP adapter",
"type": "object",
"properties": {
"tspId": {
"$ref": "http://maasglobal.com/core/booking.json#/definitions/tspId"
"allOf": [
{
"type": "object",
"properties": {
"tspId": {
"$ref": "http://maasglobal.com/core/booking.json#/definitions/tspId"
}
},
"required": [
"tspId"
]
},
{
"$ref": "http://maasglobal.com/core/components/personal-data-query.json#/definitions/queryParams"
}
},
"required": ["tspId"],
"additionalProperties": false
]
}
22 changes: 22 additions & 0 deletions test/feature-validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ const validBookingResponseWithPaymentParameters = require('./valid-booking-respo
const validBookingResponse = require('./valid-booking-response.json');
const bookingTicketRequestRequest = require('./valid-booking-ticket-request.json');
const bookingTicketRequestResponse = require('./valid-booking-ticket-response.json');

const tspReadByIdRequest = require('./valid-tsp-read-by-id-request.json');
const tspOptionsListRequest = require('./valid-tsp-options-list-request.json');

// Missing required fields [leg, customer, token] etc...
const invalidBookingResponse = {
customer: {
Expand Down Expand Up @@ -81,4 +85,22 @@ describe('Schema validation', () => {
expect(validated.paymentParameters).to.deep.equal(validBookingResponseWithPaymentParameters.paymentParameters);
});
});

describe('validate schemas TSP read by id query parameters', () => {
const schema = require('../schemas/tsp/booking-read-by-id/request.json');

it('should succeed without error', () => {
const validated = utils.validate(schema, tspReadByIdRequest);
expect(validated).to.deep.equal(tspReadByIdRequest);
});
});

describe('validate schemas TSP options list query parameters', () => {
const schema = require('../schemas/tsp/booking-options-list/request.json');

it('should succeed without error', () => {
const validated = utils.validate(schema, tspOptionsListRequest);
expect(validated).to.deep.equal(tspOptionsListRequest);
});
});
});
10 changes: 10 additions & 0 deletions test/valid-tsp-options-list-request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"mode": "TAXI",
"startTime": 1475850000000,
"endTime": 1475860000000,
"from": "-60.00,24.00",
"to": "+60.05,-24.05",
"fromRadius": 100,
"toRadius": 10,
"customer[opaqueId]": "1ca7f4853bc724caa58645552c842ea21543337fe4731e2222de7e1668e1facf"
}
5 changes: 5 additions & 0 deletions test/valid-tsp-read-by-id-request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"tspId": "tsp-id",
"customer[notInSchema]": "passedThrough",
"customer[phone]": "+35855544433"
}