mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 09:09:36 +02:00
Add build trigger for validation-tools
GitOrigin-RevId: 04299d9ab23c65aa791acecd1c0e63b70df9a8d1
This commit is contained in:
21
.editorconfig
Normal file
21
.editorconfig
Normal file
@@ -0,0 +1,21 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
|
||||
[*.{pug,coffee}]
|
||||
indent_style = tab
|
||||
|
||||
[*.{pug,patch}]
|
||||
trim_trailing_whitespace = false
|
||||
1
libraries/validation-tools/.nvmrc
Normal file
1
libraries/validation-tools/.nvmrc
Normal file
@@ -0,0 +1 @@
|
||||
22.18.0
|
||||
5
libraries/validation-tools/Errors.js
Normal file
5
libraries/validation-tools/Errors.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const OError = require('@overleaf/o-error')
|
||||
|
||||
class ParamsError extends OError {}
|
||||
|
||||
module.exports = { ParamsError }
|
||||
10
libraries/validation-tools/buildscript.txt
Normal file
10
libraries/validation-tools/buildscript.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
validation-tools
|
||||
--dependencies=None
|
||||
--docker-repos=us-east1-docker.pkg.dev/overleaf-ops/ol-docker
|
||||
--env-add=
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.18.0
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
11
libraries/validation-tools/index.js
Normal file
11
libraries/validation-tools/index.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const { ParamsError } = require('./Errors')
|
||||
const { z } = require('zod')
|
||||
const { zz } = require('./zodHelpers')
|
||||
const { validateReq } = require('./validateReq')
|
||||
|
||||
module.exports = {
|
||||
z,
|
||||
zz,
|
||||
validateReq,
|
||||
ParamsError,
|
||||
}
|
||||
31
libraries/validation-tools/package.json
Normal file
31
libraries/validation-tools/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "@overleaf/validation-tools",
|
||||
"homepage": "www.overleaf.com",
|
||||
"description": "Validation tools that can be used in a service.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/overleaf/overleaf"
|
||||
},
|
||||
"main": "index.js",
|
||||
"license": "AGPL-3.0-only",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"test": "npm run lint && npm run format && npm run types:check && npm run test:unit",
|
||||
"format": "prettier --list-different $PWD/'**/*.{js,cjs,ts}'",
|
||||
"format:fix": "prettier --write $PWD/'**/*.{js,cjs,ts}'",
|
||||
"lint": "eslint --ext .js --ext .cjs --ext .ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .js --ext .cjs --ext .ts .",
|
||||
"test:ci": "npm run test:unit",
|
||||
"test:unit": "vitest test/unit/src/*.test.ts --isolate=false",
|
||||
"types:check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@overleaf/o-error": "*",
|
||||
"mongodb": "^6.12.0",
|
||||
"zod": "^4.1.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.0.4",
|
||||
"vitest": "^3.2.4"
|
||||
}
|
||||
}
|
||||
85
libraries/validation-tools/test/unit/src/validateReq.test.ts
Normal file
85
libraries/validation-tools/test/unit/src/validateReq.test.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { validateReq } from '../../../validateReq'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { z } from 'zod'
|
||||
import type { Request } from 'express'
|
||||
import { zz } from '../../../zodHelpers'
|
||||
|
||||
describe('validateReq', () => {
|
||||
describe('with a request that is valid for the schema', () => {
|
||||
it('should return the parsed request', () => {
|
||||
const req = {
|
||||
params: {
|
||||
id: '507f1f77bcf86cd799439011',
|
||||
},
|
||||
body: {
|
||||
name: 'Valid Name',
|
||||
},
|
||||
} as Request<{ id: string }, any, { name: string }>
|
||||
|
||||
const schema = z.object({
|
||||
params: z.object({
|
||||
id: zz.objectId(),
|
||||
}),
|
||||
body: z.object({
|
||||
name: z.string(),
|
||||
}),
|
||||
})
|
||||
|
||||
const result = validateReq(req, schema)
|
||||
|
||||
expect(result).toEqual({
|
||||
params: {
|
||||
id: '507f1f77bcf86cd799439011',
|
||||
},
|
||||
body: {
|
||||
name: 'Valid Name',
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
describe('with a request that is not valid for the schema', () => {
|
||||
it('should throw NotFoundError if params are invalid', () => {
|
||||
const req = {
|
||||
params: {
|
||||
id: 'invalid-object-id',
|
||||
},
|
||||
} as Request<{ id: string }>
|
||||
|
||||
expect(() =>
|
||||
validateReq(
|
||||
req,
|
||||
z.object({
|
||||
params: z.object({
|
||||
id: zz.objectId(),
|
||||
}),
|
||||
})
|
||||
)
|
||||
).toThrowError(expect.objectContaining({ name: 'ParamsError' }))
|
||||
})
|
||||
|
||||
it('should throw an error containing issues if the schema is invalid', () => {
|
||||
const req = {
|
||||
body: {
|
||||
name: 1234,
|
||||
},
|
||||
} as Request
|
||||
|
||||
expect(() =>
|
||||
validateReq(
|
||||
req,
|
||||
z.object({
|
||||
body: z.object({
|
||||
name: z.string(),
|
||||
}),
|
||||
})
|
||||
)
|
||||
).toThrowError(
|
||||
expect.objectContaining({
|
||||
issues: expect.arrayContaining([
|
||||
expect.objectContaining({ path: ['body', 'name'] }),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
44
libraries/validation-tools/test/unit/src/zodHelpers.test.ts
Normal file
44
libraries/validation-tools/test/unit/src/zodHelpers.test.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { zz } from '../../../zodHelpers'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import mongodb from 'mongodb'
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
describe('zodHelpers', () => {
|
||||
describe('objectId', () => {
|
||||
it('fails to parse when provided with an invalid ObjectId', () => {
|
||||
const parsed = zz.objectId().safeParse('aa')
|
||||
expect(parsed.success).toBe(false)
|
||||
expect(parsed.error?.issues).toHaveLength(1)
|
||||
expect(parsed.error?.issues).toMatchObject([
|
||||
expect.objectContaining({
|
||||
message: 'invalid Mongo ObjectId',
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it('parses successfully when provided with a valid ObjectId', () => {
|
||||
const parsed = zz.objectId().safeParse('507f1f77bcf86cd799439011')
|
||||
expect(parsed.success).toBe(true)
|
||||
expect(parsed.data).toBe('507f1f77bcf86cd799439011')
|
||||
})
|
||||
})
|
||||
describe('coercedObjectId', () => {
|
||||
it('fails to parse when provided with an invalid ObjectId', () => {
|
||||
const parsed = zz.coercedObjectId().safeParse('aa')
|
||||
expect(parsed.success).toBe(false)
|
||||
expect(parsed.error?.issues).toHaveLength(1)
|
||||
expect(parsed.error?.issues).toMatchObject([
|
||||
expect.objectContaining({
|
||||
message: 'invalid Mongo ObjectId',
|
||||
}),
|
||||
])
|
||||
})
|
||||
it('parses to an ObjectId when provided with a valid ObjectId string', () => {
|
||||
const parsed = zz.coercedObjectId().safeParse('507f1f77bcf86cd799439011')
|
||||
expect(parsed.success).toBe(true)
|
||||
expect(parsed.data).toBeInstanceOf(ObjectId)
|
||||
expect(parsed.data?.toString()).toBe('507f1f77bcf86cd799439011')
|
||||
})
|
||||
})
|
||||
})
|
||||
11
libraries/validation-tools/tsconfig.json
Normal file
11
libraries/validation-tools/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.backend.json",
|
||||
"compilerOptions": {
|
||||
"allowImportingTsExtensions": true
|
||||
},
|
||||
"include": [
|
||||
"**/*.js",
|
||||
"**/*.ts",
|
||||
"**/*.cjs"
|
||||
]
|
||||
}
|
||||
34
libraries/validation-tools/validateReq.js
Normal file
34
libraries/validation-tools/validateReq.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// @ts-check
|
||||
const { ParamsError } = require('./Errors')
|
||||
|
||||
/**
|
||||
* @typedef {import('zod').ZodType} ZodType
|
||||
* @typedef {import('express').Request} Request
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {import('zod').output<T>} output<T>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Validate a request against a zod schema
|
||||
*
|
||||
* @template {ZodType} T
|
||||
* @param {Request} req - The Express request object
|
||||
* @param {T} schema - The Zod schema to validate against
|
||||
* @returns {output<T>} The validated request object
|
||||
*/
|
||||
function validateReq(req, schema) {
|
||||
const parsed = schema.safeParse(req)
|
||||
if (parsed.success) {
|
||||
return parsed.data
|
||||
} else if (parsed.error.issues.some(issue => issue.path[0] === 'params')) {
|
||||
// Parts of the URL path failed to validate; throw a specific error
|
||||
throw new ParamsError('Invalid params').withCause(parsed.error)
|
||||
} else {
|
||||
throw parsed.error
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { validateReq }
|
||||
17
libraries/validation-tools/zodHelpers.js
Normal file
17
libraries/validation-tools/zodHelpers.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const { z } = require('zod')
|
||||
const mongodb = require('mongodb')
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
const zz = {
|
||||
objectId: () =>
|
||||
z.string().refine(ObjectId.isValid, { message: 'invalid Mongo ObjectId' }),
|
||||
coercedObjectId: () =>
|
||||
z
|
||||
.string()
|
||||
.refine(ObjectId.isValid, { message: 'invalid Mongo ObjectId' })
|
||||
.transform(val => new ObjectId(val)),
|
||||
hex: () => z.string().regex(/^[0-9a-f]*$/),
|
||||
}
|
||||
|
||||
module.exports = { zz }
|
||||
Reference in New Issue
Block a user