[project-history] update schemas to accept numeric project IDs

GitOrigin-RevId: 4dac763223f42eb772c30f34e0da0d22464055dd
This commit is contained in:
Domagoj Kriskovic
2026-01-12 15:41:10 +01:00
committed by Copybot
parent da9da896c2
commit 859c21b4dd
2 changed files with 159 additions and 20 deletions

View File

@@ -63,7 +63,7 @@ export function initializeProject(req, res, next) {
const flushProjectSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
}),
query: z.object({
debug: z.stringbool().default(false),
@@ -110,7 +110,7 @@ export function flushProject(req, res, next) {
const dumpProjectSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
}),
query: z.object({
count: z.coerce.number().int().optional(),
@@ -165,7 +165,7 @@ export function flushOld(req, res, next) {
const getDiffSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
}),
query: z.object({
pathname: z.string(),
@@ -190,7 +190,7 @@ export function getDiff(req, res, next) {
const getFileTreeDiffSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
}),
query: z.object({
from: z.coerce.number().int(),
@@ -213,7 +213,7 @@ export function getFileTreeDiff(req, res, next) {
const getUpdatesSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
}),
query: z.object({
before: z.coerce.number().int().optional(),
@@ -246,7 +246,7 @@ export function getUpdates(req, res, next) {
const latestVersionSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
}),
})
@@ -282,7 +282,7 @@ export function latestVersion(req, res, next) {
const getFileSnapshotSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
version: z.coerce.number().int(),
pathname: z.string(),
}),
@@ -309,7 +309,7 @@ export function getFileSnapshot(req, res, next) {
const getRangesSnapshotSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
version: z.coerce.number().int(),
pathname: z.string(),
}),
@@ -333,7 +333,7 @@ export function getRangesSnapshot(req, res, next) {
const getFileMetadataSnapshotSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
version: z.coerce.number().int(),
pathname: z.string(),
}),
@@ -357,7 +357,7 @@ export function getFileMetadataSnapshot(req, res, next) {
const getLatestSnapshotSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
}),
})
@@ -382,7 +382,7 @@ export function getLatestSnapshot(req, res, next) {
const getChangesInChunkSinceSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
}),
query: z.object({
since: z.coerce.number().int().min(0),
@@ -415,7 +415,7 @@ export function getChangesInChunkSince(req, res, next) {
const getProjectSnapshotSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
version: z.coerce.number().int(),
}),
})
@@ -437,7 +437,7 @@ export function getProjectSnapshot(req, res, next) {
const getPathsAtVersionSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
version: z.coerce.number().int(),
}),
})
@@ -477,7 +477,7 @@ export function checkLock(req, res) {
const resyncProjectSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
}),
query: z.object({
force: z.stringbool().default(false),
@@ -536,7 +536,7 @@ export function resyncProject(req, res, next) {
const forceDebugProjectSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
}),
query: z.object({
clear: z.stringbool().default(false),
@@ -582,7 +582,7 @@ export function getQueueCounts(req, res, next) {
const getLabelsSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
}),
})
@@ -611,7 +611,7 @@ export function getLabels(req, res, next) {
const createLabelSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
user_id: zz.objectId().optional(),
}),
body: z.object({
@@ -683,7 +683,7 @@ export function createLabel(req, res, next) {
*/
const deleteLabelForUserSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
user_id: zz.objectId(),
label_id: zz.objectId(),
}),
@@ -703,7 +703,7 @@ export function deleteLabelForUser(req, res, next) {
const deleteLabelSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
label_id: zz.objectId(),
}),
})
@@ -785,7 +785,7 @@ export function transferLabels(req, res, next) {
const deleteProjectSchema = z.object({
params: z.object({
project_id: zz.objectId(),
project_id: zz.objectId().or(z.coerce.number()),
}),
})

View File

@@ -0,0 +1,139 @@
import { expect } from 'chai'
import nock from 'nock'
import request from 'request'
import * as ProjectHistoryApp from './helpers/ProjectHistoryApp.js'
import * as ProjectHistoryClient from './helpers/ProjectHistoryClient.js'
const MockHistoryStore = () => nock('http://127.0.0.1:3100')
const MockWeb = () => nock('http://127.0.0.1:3000')
const fixture = path => new URL(`../fixtures/${path}`, import.meta.url)
/**
* These tests verify that endpoints accept numeric project IDs (in addition to ObjectId strings).
* This is needed for v1 history projects which use numeric project IDs.
*/
describe('NumericProjectId', function () {
beforeEach(async function () {
await ProjectHistoryApp.ensureRunning()
// Use a numeric project ID (simulating v1 history projects)
this.numericProjectId = 123456
MockHistoryStore().post('/api/projects').reply(200, {
projectId: this.numericProjectId,
})
const olProject = await ProjectHistoryClient.initializeProject(
this.numericProjectId
)
this.historyId = olProject.id
MockWeb()
.get(`/project/${this.numericProjectId}/details`)
.reply(200, {
name: 'Test Project',
overleaf: { history: { id: this.historyId } },
})
.persist()
MockHistoryStore()
.get(`/api/projects/${this.historyId}/latest/history`)
.replyWithFile(200, fixture('chunks/7-8.json'))
.persist()
MockHistoryStore()
.get(`/api/projects/${this.historyId}/versions/7/history`)
.replyWithFile(200, fixture('chunks/7-8.json'))
.persist()
MockHistoryStore()
.get(`/api/projects/${this.historyId}/versions/8/history`)
.replyWithFile(200, fixture('chunks/7-8.json'))
.persist()
})
afterEach(function () {
nock.cleanAll()
})
function makeRequest(options) {
return new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if (error) return reject(error)
resolve({ response, body })
})
})
}
it('should accept numeric project_id for flush', async function () {
const { response } = await makeRequest({
method: 'POST',
url: `http://127.0.0.1:3054/project/${this.numericProjectId}/flush`,
})
expect(response.statusCode).to.equal(204)
})
it('should accept numeric project_id for dump', async function () {
const { response } = await makeRequest({
method: 'GET',
url: `http://127.0.0.1:3054/project/${this.numericProjectId}/dump`,
})
expect(response.statusCode).to.equal(200)
})
it('should accept numeric project_id for filetree diff', async function () {
const { response } = await makeRequest({
method: 'GET',
url: `http://127.0.0.1:3054/project/${this.numericProjectId}/filetree/diff`,
qs: { from: 7, to: 8 },
})
expect(response.statusCode).to.equal(200)
})
it('should accept numeric project_id for updates', async function () {
const { response } = await makeRequest({
method: 'GET',
url: `http://127.0.0.1:3054/project/${this.numericProjectId}/updates`,
qs: { min_count: 1 },
})
expect(response.statusCode).to.equal(200)
})
it('should accept numeric project_id for version', async function () {
const { response } = await makeRequest({
method: 'GET',
url: `http://127.0.0.1:3054/project/${this.numericProjectId}/version`,
})
expect(response.statusCode).to.equal(200)
})
it('should accept numeric project_id for snapshot', async function () {
const { response } = await makeRequest({
method: 'GET',
url: `http://127.0.0.1:3054/project/${this.numericProjectId}/snapshot`,
})
expect(response.statusCode).to.equal(200)
})
it('should accept numeric project_id for getLabels', async function () {
const { response } = await makeRequest({
method: 'GET',
url: `http://127.0.0.1:3054/project/${this.numericProjectId}/labels`,
})
expect(response.statusCode).to.equal(200)
})
it('should accept numeric project_id for createLabel', async function () {
const { response } = await makeRequest({
method: 'POST',
url: `http://127.0.0.1:3054/project/${this.numericProjectId}/labels`,
json: {
comment: 'test label',
version: 7,
user_id: '507f1f77bcf86cd799439011',
},
})
expect(response.statusCode).to.equal(200)
})
})