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

Soft deletion script for models #1919

Merged
merged 4 commits into from
Mar 5, 2025
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
18 changes: 14 additions & 4 deletions backend/src/models/Inference.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Document, model, Schema } from 'mongoose'
import { model, Schema } from 'mongoose'
import MongooseDelete, { SoftDeleteDocument } from 'mongoose-delete'

export interface InferenceSetting {
processorType: string
Expand All @@ -15,14 +16,16 @@ export interface InferenceInterface {

settings: InferenceSetting

deleted: boolean

createdBy: string
createdAt: Date
updatedAt: Date
}

export type InferenceDoc = InferenceInterface & Document<any, any, InferenceInterface>
export type InferenceDoc = InferenceInterface & SoftDeleteDocument

const InferenceSchema = new Schema<InferenceInterface>(
const InferenceSchema = new Schema<InferenceDoc>(
{
modelId: { type: String, required: true },
image: { type: String, required: true },
Expand Down Expand Up @@ -55,8 +58,15 @@ const InferenceSchema = new Schema<InferenceInterface>(
},
)

InferenceSchema.plugin(MongooseDelete, {
overrideMethods: 'all',
deletedBy: true,
deletedByType: String,
deletedAt: true,
})

InferenceSchema.index({ modelId: 1, image: 1, tag: 1 }, { unique: true })

const InferenceModel = model<InferenceInterface>('v2_Model_Inference', InferenceSchema)
const InferenceModel = model<InferenceDoc>('v2_Model_Inference', InferenceSchema)

export default InferenceModel
17 changes: 13 additions & 4 deletions backend/src/models/ModelCardRevision.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Document, model, Schema } from 'mongoose'
import { model, Schema } from 'mongoose'
import MongooseDelete, { SoftDeleteDocument } from 'mongoose-delete'

import { ModelCardInterface } from './Model.js'

Expand All @@ -7,14 +8,15 @@ import { ModelCardInterface } from './Model.js'
// client.
export interface ModelCardRevisionInterface extends ModelCardInterface {
modelId: string
deleted: boolean
}

// The doc type includes all values in the plain interface, as well as all the
// properties and functions that Mongoose provides. If a function takes in an
// object from Mongoose it should use this interface
export type ModelCardRevisionDoc = ModelCardRevisionInterface & Document<any, any, ModelCardRevisionInterface>
export type ModelCardRevisionDoc = ModelCardRevisionInterface & SoftDeleteDocument

const ModelCardRevisionSchema = new Schema<ModelCardRevisionInterface>(
const ModelCardRevisionSchema = new Schema<ModelCardRevisionDoc>(
{
modelId: { type: String, required: true },
schemaId: { type: String, required: true },
Expand All @@ -36,6 +38,13 @@ const ModelCardRevisionSchema = new Schema<ModelCardRevisionInterface>(
// to learn more.
ModelCardRevisionSchema.index({ modelId: 1, version: 1 }, { unique: true })

const ModelCardRevisionModel = model<ModelCardRevisionInterface>('v2_Model_Card_Revision', ModelCardRevisionSchema)
ModelCardRevisionSchema.plugin(MongooseDelete, {
overrideMethods: 'all',
deletedBy: true,
deletedByType: String,
deletedAt: true,
})

const ModelCardRevisionModel = model<ModelCardRevisionDoc>('v2_Model_Card_Revision', ModelCardRevisionSchema)

export default ModelCardRevisionModel
79 changes: 79 additions & 0 deletions backend/src/scripts/modelSoftDelete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import AccessRequestModel from '../models/AccessRequest.js'
import FileModel from '../models/File.js'
import InferenceModel from '../models/Inference.js'
import ModelModel from '../models/Model.js'
import ModelCardRevisionModel from '../models/ModelCardRevision.js'
import ReleaseModel from '../models/Release.js'
import ResponseModel from '../models/Response.js'
import ReviewModel from '../models/Review.js'
import WebhookModel from '../models/Webhook.js'
import log from '../services/log.js'
import { connectToMongoose } from '../utils/database.js'

async function script() {
const modelId = process.argv.slice(2)

if (!modelId || !process.argv[2]) {
log.error('No model ID option. Please use format "npm run script -- modelSoftDelete <model-id>"')
return
}

await connectToMongoose()

const model = await ModelModel.findOne({ id: modelId })
if (!model) {
log.error(`Cannot find model using ID ${modelId}`)
return
}
const releases = await ReleaseModel.find({ modelId })
log.info(`Deleting ${releases.length} releases`)
for (const release of releases) {
await release.delete()
}

const accesses = await AccessRequestModel.find({ modelId })
log.info(`Deleting ${accesses.length} access requests`)
for (const access of accesses) {
await access.delete()
}

const revisions = await ModelCardRevisionModel.find({ modelId })
log.info(`Deleting ${revisions.length} model card revisions`)
for (const revision of revisions) {
await revision.delete()
}

const reviews = await ReviewModel.find({ modelId })
log.info(`Deleting ${reviews.length} reviews`)
for (const review of reviews) {
const responses = await ResponseModel.find({ parentId: review._id })
log.info(`Deleting ${responses.length} responses from review ${review._id} `)
for (const response of responses) {
await response.delete()
}
await review.delete()
}

const files = await FileModel.find({ modelId })
log.info(`Deleting ${accesses.length} files`)
for (const file of files) {
await file.delete()
}

const webhooks = await WebhookModel.find({ modelId })
log.info(`Deleting ${webhooks.length} webhooks`)
for (const webhook of webhooks) {
await webhook.delete()
}

const inferences = await InferenceModel.find({ modelId })
log.info(`Deleting ${inferences.length} inferences`)
for (const inference of inferences) {
await inference.delete()
}

await model.delete()
return
}

script()
5 changes: 3 additions & 2 deletions backend/src/scripts/runScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ const { exec } = shelljs
// npx tsx src/scripts/uploadExampleModel.ts
export default async function runScript() {
const argv = await yargs(hideBin(process.argv)).usage('Usage: $0 [script]').argv
const script = argv._
const script = argv._[0]
const args = argv._.slice(1)

exec(`npx tsx src/scripts/${script}.ts`)
exec(`npx tsx src/scripts/${script}.ts ${args}`)
}

runScript()
2 changes: 1 addition & 1 deletion backend/src/services/modelCardExport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export async function getModelCardHtml(
throw new Error('Failed to find model card to export.')
}

const modelCardRevision: ModelCardRevisionInterface = { ...modelCard, modelId }
const modelCardRevision: ModelCardRevisionInterface = { ...modelCard, modelId, deleted: model.deleted }
const html = await renderToHtml(model, modelCardRevision)

return { html, modelCard }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ exports[`routes > model > putModelCard > 200 > ok 1`] = `
{
"card": {
"_id": "6776901b879d08e34b599d7e",
"deleted": false,
"id": "6776901b879d08e34b599d7e",
"modelId": "test",
},
Expand All @@ -15,6 +16,7 @@ exports[`routes > model > putModelCard > audit > expected call 1`] = `"vespillo"
exports[`routes > model > putModelCard > audit > expected call 2`] = `
{
"_id": "6776901b879d08e34b599d7e",
"deleted": false,
"modelId": "test",
}
`;
20 changes: 0 additions & 20 deletions backend/test/routes/schema/__snapshots__/patchSchema.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,6 @@ exports[`routes > schema > patchSchema > audit > expected call 1`] = `
}
`;

exports[`routes > schema > patchSchema > patchSchema > audit > expected call 1`] = `
{
"active": false,
"description": "vespillo",
"hidden": false,
"name": "Jaime Hilll",
}
`;

exports[`routes > schema > patchSchema > patchSchema > successfully updates the schema 1`] = `
{
"schema": {
"active": false,
"description": "vespillo",
"hidden": false,
"name": "Jaime Hilll",
},
}
`;

exports[`routes > schema > patchSchema > successfully updates the schema 1`] = `
{
"schema": {
Expand Down
1 change: 1 addition & 0 deletions backend/test/services/modelCardExport.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe('services > export', () => {
version: 1,
createdBy: 'Joe Bloggs',
metadata: {},
deleted: false,
}
const mockSchema = { jsonSchema: { type: 'object', properties: {} } }

Expand Down
Loading