-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #117 from klarna-incubator/validation-endpoint
feature: add validation endpoint
- Loading branch information
Showing
19 changed files
with
673 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { DataAccessLayer } from "@gram/core/dist/data/dal.js"; | ||
import express from "express"; | ||
import { errorWrap } from "../../../../util/errorHandler.js"; | ||
import { validateModel } from "./validateModel.js"; | ||
|
||
export function validationRouter(dal: DataAccessLayer): express.Router { | ||
const router = express.Router(); | ||
|
||
router.get("/:id", errorWrap(validateModel(dal))); | ||
return router; | ||
} |
193 changes: 193 additions & 0 deletions
193
api/src/resources/gram/v1/validation/validateModel.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
import request from "supertest"; | ||
import { jest } from "@jest/globals"; | ||
import { createTestApp } from "../../../../test-util/app.js"; | ||
import { sampleUserToken } from "../../../../test-util/sampleTokens.js"; | ||
import { ModelDataService } from "@gram/core/dist/data/models/ModelDataService.js"; | ||
import Model from "@gram/core/dist/data/models/Model.js"; | ||
import { createSampleModel } from "../../../../test-util/model.js"; | ||
import { DataAccessLayer } from "@gram/core/dist/data/dal.js"; | ||
import { | ||
ValidationProvider, | ||
ValidationResult, | ||
} from "@gram/core/dist/validation/ValidationHandler.js"; | ||
import { log } from "console"; | ||
|
||
describe("validateModel", () => { | ||
let app: any; | ||
let token: string; | ||
let getById: any; | ||
let modelService: ModelDataService; | ||
let dal: DataAccessLayer; | ||
|
||
beforeAll(async () => { | ||
({ app, dal } = await createTestApp()); | ||
token = await sampleUserToken(); | ||
modelService = dal.modelService; | ||
getById = jest.spyOn(modelService, "getById"); | ||
}); | ||
|
||
it("should register StaticValidationHandler", () => { | ||
const validationProviderList = dal.validationHandler.validationProviders; | ||
expect(validationProviderList.length).toBeGreaterThan(0); | ||
expect( | ||
validationProviderList.find((element: ValidationProvider) => { | ||
return element.name === "StaticValidationProvider"; | ||
}) | ||
).toBeTruthy(); | ||
}); | ||
|
||
it("should return 401 on un-authenticated request", async () => { | ||
const res = await request(app).get("/api/v1/validate/12323"); | ||
expect(res.status).toBe(401); | ||
}); | ||
it("should return 401 when using invalid user token", async () => { | ||
const res = await request(app) | ||
.get("/api/v1/validate/12323") | ||
.set("Authorization", "invalidtoken"); | ||
expect(res.status).toBe(401); | ||
}); | ||
it("should return 400 if the model id is invalid or unknown", async () => { | ||
const invalidmodelId = "12323"; | ||
const res = await request(app) | ||
.get("/api/v1/validate/" + invalidmodelId) | ||
.set("Authorization", token); | ||
expect(res.status).toBe(400); | ||
}); | ||
|
||
it("should return 200 if the model id is valid", async () => { | ||
const validModelId = await createSampleModel(dal); | ||
|
||
getById.mockImplementation(async () => { | ||
const model = new Model(validModelId, "some-version", "some-owner"); | ||
return model; | ||
}); | ||
|
||
const res = await request(app) | ||
.get("/api/v1/validate/" + validModelId) | ||
.set("Authorization", token); | ||
expect(res.status).toBe(200); | ||
}); | ||
|
||
it("should return a list of validation results", async () => { | ||
const validModelId = "1daf8acc-a691-408d-802c-46e360d2f427"; | ||
getById.mockImplementation(async () => { | ||
const model = new Model("some-system-id", "some-version", "some-owner"); | ||
model.id = validModelId; | ||
model.data = { | ||
dataFlows: [], | ||
components: [ | ||
{ | ||
x: 222, | ||
y: 350, | ||
id: "5e8e6021-9455-4157-a026-c7be218b1019", | ||
name: "Process", | ||
type: "proc", | ||
}, | ||
], | ||
}; | ||
return model; | ||
}); | ||
|
||
const res = await request(app) | ||
.get("/api/v1/validate/" + validModelId) | ||
.set("Authorization", token); | ||
expect(res.status).toBe(200); | ||
expect(res.body.id).toBe(validModelId); | ||
expect(res.body.total).toBe(res.body.results.length); | ||
expect(res.body.results).toBeDefined(); | ||
|
||
// expect (res.body.data) | ||
}); | ||
|
||
it("should return a list of model validation results only if model is empty", async () => { | ||
const validModelId = "1daf8acc-a691-408d-802c-46e360d2f427"; | ||
getById.mockImplementation(async () => { | ||
const model = new Model("some-system-id", "some-version", "some-owner"); | ||
model.id = validModelId; | ||
model.data = { | ||
dataFlows: [], | ||
components: [], | ||
}; | ||
return model; | ||
}); | ||
|
||
const res = await request(app) | ||
.get("/api/v1/validate/" + validModelId) | ||
.set("Authorization", token); | ||
console.log("body", res.body.results); | ||
|
||
const results = res.body.results; | ||
|
||
expect(res.status).toBe(200); | ||
expect(results.length).not.toBe(0); | ||
expect(res.body.id).toBe(validModelId); | ||
expect(res.body.total).toBe(results.length); | ||
expect(Array.isArray(results)).toBeTruthy(); | ||
|
||
expect( | ||
results.every((element: ValidationResult) => element.type === "model") | ||
).toBeTruthy(); | ||
expect( | ||
results.some((element: ValidationResult) => element.type === "component") | ||
).toBeFalsy(); | ||
}); | ||
|
||
it("should return a list of component and model validation results if model has components", async () => { | ||
const validModelId = "1daf8acc-a691-408d-802c-46e360d2f427"; | ||
getById.mockImplementation(async () => { | ||
const model = new Model("some-system-id", "some-version", "some-owner"); | ||
model.id = validModelId; | ||
model.data = { | ||
dataFlows: [], | ||
components: [ | ||
{ | ||
x: 222, | ||
y: 350, | ||
id: "5e8e6021-9455-4157-a026-c7be218b1019", | ||
name: "Process", | ||
type: "proc", | ||
}, | ||
{ | ||
x: 222, | ||
y: 350, | ||
id: "5e8e7021-9455-4157-z026-c7be218b1019", | ||
name: "Process", | ||
type: "proc", | ||
}, | ||
], | ||
}; | ||
return model; | ||
}); | ||
|
||
const res = await request(app) | ||
.get("/api/v1/validate/" + validModelId) | ||
.set("Authorization", token); | ||
const results = res.body.results; | ||
|
||
expect(res.status).toBe(200); | ||
expect(results.length).not.toBe(0); | ||
expect(res.body.id).toBe(validModelId); | ||
expect(res.body.total).toBe(results.length); | ||
expect(Array.isArray(results)).toBeTruthy(); | ||
|
||
expect( | ||
results.some( | ||
(element: ValidationResult) => | ||
element.type === "component" && | ||
element.elementId === "5e8e6021-9455-4157-a026-c7be218b1019" | ||
) | ||
).toBeTruthy(); | ||
|
||
expect( | ||
results.some( | ||
(element: ValidationResult) => | ||
element.type === "component" && | ||
element.elementId === "5e8e7021-9455-4157-z026-c7be218b1019" | ||
) | ||
).toBeTruthy(); | ||
|
||
expect( | ||
results.some((element: ValidationResult) => element.type === "model") | ||
).toBeTruthy(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/** | ||
* GET /api/v1/validate/{id} | ||
* @exports {function} handler | ||
*/ | ||
import { DataAccessLayer } from "@gram/core/dist/data/dal.js"; | ||
import Model from "@gram/core/dist/data/models/Model.js"; | ||
import { Request, Response } from "express"; | ||
|
||
export function validateModel(dal: DataAccessLayer) { | ||
return async (req: Request, res: Response) => { | ||
const modelId = req.params.id; | ||
let model: Model | null = null; | ||
// Get the model from the DAL | ||
try { | ||
model = await dal.modelService.getById(modelId); | ||
|
||
if (!model) { | ||
return res.sendStatus(400); | ||
} | ||
} catch (error) { | ||
return res.sendStatus(400); | ||
} | ||
// Validate the model | ||
try { | ||
// Use validation service to validate the model | ||
// Return the validation results + model id | ||
|
||
const validationResults = await dal.validationHandler.validate(model); | ||
console.log("Results from static validation", validationResults); | ||
|
||
return res.json({ | ||
id: modelId, | ||
total: validationResults.length, | ||
results: validationResults, | ||
}); | ||
} catch (error) { | ||
return res.sendStatus(400); | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
import { DataAccessLayer } from "@gram/core/dist/data/dal.js"; | ||
import Model from "@gram/core/dist/data/models/Model.js"; | ||
import Model, { ModelData } from "@gram/core/dist/data/models/Model.js"; | ||
import { sampleOwnedSystem } from "./sampleOwnedSystem.js"; | ||
|
||
export async function createSampleModel( | ||
dal: DataAccessLayer, | ||
owner: string = "root" | ||
owner: string = "root", | ||
data: ModelData = { components: [], dataFlows: [] } | ||
) { | ||
const model = new Model(sampleOwnedSystem.id, "some-version", owner); | ||
model.data = { components: [], dataFlows: [] }; | ||
model.data = data; | ||
const modelId = await dal.modelService.create(model); | ||
return modelId; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import type { Config } from "@jest/types"; | ||
|
||
const config: Config.InitialOptions = { | ||
preset: "ts-jest/presets/default-esm", | ||
extensionsToTreatAsEsm: [".ts"], | ||
testEnvironment: "node", | ||
verbose: true, | ||
transform: { | ||
"^.+\\.tsx?$": [ | ||
"ts-jest", | ||
{ | ||
tsconfig: "./tsconfig.json", | ||
useESM: true, | ||
}, | ||
], | ||
}, | ||
moduleNameMapper: { | ||
"^(\\.{1,2}/.*)\\.js$": "$1", | ||
}, | ||
coverageProvider: "v8", | ||
transformIgnorePatterns: ["<rootDir>/node_modules/"], | ||
}; | ||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.