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

feat: Validator summary function #101

Merged
merged 7 commits into from
Aug 14, 2024
Merged
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
72 changes: 67 additions & 5 deletions docs/gbfs-validator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ info:
email: [email protected]
license:
name: MobilityData License
url: https://www.apache.org/licenses/LICENSE-2.0
url: https://www.apache.org/licenses/LICENSE-2.0
servers:
- url: https://gbfs-validator.netlify.app/.netlify/functions
description: Production release environment
description: Production release environment
- url: http://localhost:8888/.netlify/functions
description: Local development environment
description: Local development environment
paths:
/validator:
post:
Expand Down Expand Up @@ -41,7 +41,7 @@ paths:
/feed:
post:
summary: Get feed content
description: Get content of all GBFS's files. Usefull to avoid CORS errors.
description: Get content of all GBFS's files. Useful to avoid CORS errors.
requestBody:
content:
application/json:
Expand All @@ -61,6 +61,29 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
/validator-summary:
post:
summary: Get a summary of the validation results
description: Returns a summary from the validator's response, including grouped error details.
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ValidatorRequest'
required: true
responses:
'200':
description: Validation summary
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationSummary'
'500':
description: Error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Error:
Expand Down Expand Up @@ -162,7 +185,7 @@ components:
tokenUrl:
type: string
required:
- url
- url
FeedRequest:
required:
- url
Expand All @@ -183,3 +206,42 @@ components:
type: array
items:
type: object
ValidationSummary:
type: object
properties:
summary:
type: object
filesSummary:
type: array
items:
type: object
properties:
required:
type: boolean
exists:
type: boolean
file:
type: string
hasErrors:
type: boolean
errorsCount:
type: number
groupedErrors:
type: array
items:
type: object
properties:
keyword:
type: string
message:
type: string
schemaPath:
type: string
count:
type: number
required:
- required
- exists
- file
- hasErrors
- errorsCount
114 changes: 114 additions & 0 deletions functions/validator-summary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
const GBFS = require('gbfs-validator')

/** @typedef {{
* summary: {
* validatorVersion: string,
* hasErrors: boolean,
* errorsCount: number,
* version: {
* detected: string,
* validated: string
* }
* filesSummary: [
* {
* required: boolean,
* exists: boolean,
* hasErrors: boolean,
* file: string,
* errorsCount: number,
* groupedErrors: [
* {
* keyword: string,
* message: string,
* schemaPath: string,
* count: number
* }
* ]
* }
* ]
* }
* }} Summary
*/

/**
* This function returns a summary from the validator's response, stripping out the notices and grouping errors by message, keyword, and schemaPath.
*
* @param validationResult from the GBFS validator class
* @returns { Summary }
*/
const getSummary = (validationResult) => (
{
...validationResult,
files: undefined,
filesSummary: (validationResult.files || []).map(item => ({
required: item.required,
exists: item.exists,
file: item.file,
hasErrors: item.hasErrors,
errorsCount: item.errorsCount,
groupedErrors: item.exists && item.languages && item.languages[0] && item.languages[0].errors
? groupErrors(item.languages[0].errors)
: []
}))
}
)

/**
* Groups errors by keyword, message, and schemaPath, adding a count for each group.
*
* @param errors array of error objects
* @returns {Array} grouped errors with count
*/
const groupErrors = (errors) => {
const errorMap = {};

errors.forEach(error => {
const key = `${error.keyword}-${error.message}-${error.schemaPath}`;
if (errorMap[key]) {
errorMap[key].count += 1;
} else {
errorMap[key] = {
keyword: error.keyword,
message: error.message,
schemaPath: error.schemaPath,
count: 1
};
}
});

return Object.values(errorMap);
};


/**
* call the callback function with {@link Summary}
*/
exports.handler = function (event, context, callback) {
let body

try {
body = JSON.parse(event.body)
} catch (err) {
callback(err, {
statusCode: 500,
body: JSON.stringify(err)
})
}

const gbfs = new GBFS(body.url, body.options)

gbfs
.validation()
.then(result => {
callback(null, {
statusCode: 200,
body: JSON.stringify(getSummary(result))
})
})
.catch(err => {
callback(null, {
statusCode: 500,
body: JSON.stringify(err.message)
})
})
}
Loading