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

Project Tag Schema [PAUSED] #138

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
2,554 changes: 1,307 additions & 1,247 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@
"lodash": "^4.17.20",
"luxon": "^3.2.1",
"mongo-cursor-pagination": "^8.1.2",
"mongodb-query-parser": "^3.0.0",
"mongoose": "^7.0.0",
"mongodb-query-parser": "^4.0.0",
"mongoose": "^8.0.0",
"prompt": "^1.2.0",
"stream-transform": "^3.2.0",
"superagent": "^6.1.0"
Expand All @@ -61,8 +61,8 @@
"eslint-plugin-node": "^11.1.0",
"serverless": "^3.20.0",
"serverless-dotenv-plugin": "^6.0.0",
"serverless-offline": "^12.0.4",
"sinon": "^16.0.0",
"serverless-offline": "^13.0.0",
"sinon": "^17.0.0",
"tape": "^5.5.3",
"undici": "^5.22.0"
}
Expand Down
2 changes: 2 additions & 0 deletions src/api/auth/roles.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const MEMBER = 'project_member';
const EXPORT_DATA_ROLES = [MANAGER, MEMBER];
const WRITE_OBJECTS_ROLES = [MANAGER, MEMBER];
const WRITE_VIEWS_ROLES = [MANAGER, MEMBER];
const WRITE_COMMENTS_ROLES = [MANAGER, MEMBER];
const WRITE_IMAGES_ROLES = [MANAGER];
const DELETE_IMAGES_ROLES = [MANAGER];
const MANAGE_USERS_ROLES = [MANAGER];
Expand All @@ -13,6 +14,7 @@ const WRITE_AUTOMATION_RULES_ROLES = [MANAGER];
const WRITE_CAMERA_REGISTRATION_ROLES = [MANAGER];

export {
WRITE_COMMENTS_ROLES,
DELETE_IMAGES_ROLES,
EXPORT_DATA_ROLES,
MANAGE_USERS_ROLES,
Expand Down
99 changes: 98 additions & 1 deletion src/api/db/models/Image.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ import WirelessCamera from '../schemas/WirelessCamera.js';
import Batch from '../schemas/Batch.js';
import { CameraModel } from './Camera.js';
import { handleEvent } from '../../../automation/index.js';
import { DELETE_IMAGES_ROLES, WRITE_OBJECTS_ROLES, WRITE_IMAGES_ROLES, EXPORT_DATA_ROLES } from '../../auth/roles.js';
import {
DELETE_IMAGES_ROLES,
WRITE_OBJECTS_ROLES,
WRITE_IMAGES_ROLES,
WRITE_COMMENTS_ROLES,
EXPORT_DATA_ROLES
} from '../../auth/roles.js';
import { hasRole, buildPipeline, mapImgToDep, sanitizeMetadata, isLabelDupe, createImageAttemptRecord, createImageRecord, createLabelRecord, isImageReviewed, findActiveProjReg } from './utils.js';
import { idMatch } from './utils.js';
import { ProjectModel } from './Project.js';
Expand Down Expand Up @@ -316,6 +322,82 @@ export class ImageModel {
}
}

static async deleteComment(input, context) {
try {
const image = await ImageModel.queryById(input.imageId, context);

const comment = (image.comments || []).filter((c) => { return c._id === input.id; })[0];
if (!comment) throw new Error('Comment not found on image');

if (comment.author !== context.user['cognito:username'] && !context.user['is_superuser']) {
throw new Error('Can only edit your own comments');
}

image.comments = image.comments.filter((c) => { return c._id !== input.id; });

await image.save();

return { message: 'Images Deleted' };
} catch (err) {
// if error is uncontrolled, throw new ApolloError
if (err instanceof ApolloError) throw err;
throw new ApolloError(err);
}
}

static async updateComment(input, context) {
try {
const image = await ImageModel.queryById(input.imageId, context);

const comment = (image.comments || []).filter((c) => { return c._id === input.id; })[0];
if (!comment) throw new Error('Comment not found on image');

if (comment.author !== context.user['cognito:username'] && !context.user['is_superuser']) {
throw new Error('Can only edit your own comments');
}

comment.comment = input.comment;

await image.save();

return comment;
} catch (err) {
// if error is uncontrolled, throw new ApolloError
if (err instanceof ApolloError) throw err;
throw new ApolloError(err);
}
}

static async createComment(input, context) {
const operation = async (input) => {
return await retry(async (bail, attempt) => {
if (attempt > 1) console.log(`Retrying createComment operation! Try #: ${attempt}`);

// find images, add comment, and bulk write
return await Image.bulkWrite([{
updateOne: {
filter: { _id: input.imageId },
update: { $push: { comments: {
author: context.user['cognito:username'],
comment: input.comment
} } }
}
}]);
}, { retries: 2 });
};

try {
await operation(input);
const image = await ImageModel.queryById(input.imageId, context);

return image.comments.pop();
} catch (err) {
// if error is uncontrolled, throw new ApolloError
if (err instanceof ApolloError) throw err;
throw new ApolloError(err);
}
}

static async createObjects(input) {
const operation = async ({ objects }) => {
return await retry(async (bail, attempt) => {
Expand Down Expand Up @@ -690,6 +772,21 @@ export default class AuthedImageModel {
return await ImageModel.getLabels(projId);
}

async createComment(input, context) {
if (!hasRole(this.user, WRITE_COMMENTS_ROLES)) throw new ForbiddenError;
return await ImageModel.createComment(input, context);
}

async updateComment(input, context) {
if (!hasRole(this.user, WRITE_COMMENTS_ROLES)) throw new ForbiddenError;
return await ImageModel.updateComment(input, context);
}

async deleteComment(input, context) {
if (!hasRole(this.user, WRITE_COMMENTS_ROLES)) throw new ForbiddenError;
return await ImageModel.deleteComment(input, context);
}

async deleteImage(input, context) {
if (!hasRole(this.user, DELETE_IMAGES_ROLES)) throw new ForbiddenError;
return await ImageModel.deleteImage(input, context);
Expand Down
11 changes: 10 additions & 1 deletion src/api/db/schemas/Image.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
LocationSchema,
ObjectSchema
} from './shared/index.js';
import { randomUUID } from 'node:crypto';

const Schema = mongoose.Schema;

Expand All @@ -13,6 +14,13 @@ const Schema = mongoose.Schema;
* userSetData - user configured EXIF data
*/

const ImageCommentSchema = new Schema({
_id: { type: String, required: true, default: randomUUID },
author: { type: String, required: true },
created: { type: Date, default: Date.now, required: true },
comment: { type: String, required: true }
});

const ImageSchema = new Schema({
_id: { type: String, required: true },
bucket: { type: String, required: true },
Expand All @@ -35,7 +43,8 @@ const ImageSchema = new Schema({
model: { type: String },
location: { type: LocationSchema },
triggerSource: { type: String },
objects: { type: [ObjectSchema] }
objects: { type: [ObjectSchema] },
comments: { type: [ImageCommentSchema] }
});

ImageSchema.plugin(MongoPaging.mongoosePlugin);
Expand Down
17 changes: 16 additions & 1 deletion src/api/resolvers/Mutation.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,29 @@ const Mutation = {
return { ...res };
},

deleteImageComment: async (_, { input }, context) => {
const res = await context.models.Image.deleteComment(input, context);
return { ...res };
},

updateImageComment: async (_, { input }, context) => {
const comment = await context.models.Image.updateComment(input, context);
return { comment };
},

createImageComment: async (_, { input }, context) => {
const comment = await context.models.Image.createComment(input, context);
return { comment };
},

createImage: async (_, { input }, context) => {
const imageAttempt = await context.models.Image.createImage(input, context);
return { imageAttempt };
},

deleteImages: async (_, { input }, context) => {
const res = await context.models.Image.deleteImages(input, context);
return { ... res };
return { ...res };
},

registerCamera: async (_, { input }, context) => {
Expand Down
6 changes: 6 additions & 0 deletions src/api/type-defs/inputs/CreateImageComment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default `
input CreateImageCommentInput {
imageId: ID!
comment: String!
}
`;
6 changes: 6 additions & 0 deletions src/api/type-defs/inputs/DeleteImageComment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default `
input DeleteImageCommentInput {
imageId: ID!
id: String!
}
`;
7 changes: 7 additions & 0 deletions src/api/type-defs/inputs/UpdateImageComment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default `
input UpdateImageCommentInput {
imageId: ID!
id: String!
comment: String!
}
`;
3 changes: 2 additions & 1 deletion src/api/type-defs/objects/Image.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export default `
imageBytes: Int
mimeType: String
userSetData: JSONObject
model: String,
model: String
location: Location
objects: [Object]
comments: [ImageComment]
}`;
7 changes: 7 additions & 0 deletions src/api/type-defs/objects/ImageComment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default `
type ImageComment {
_id: ID!
created: Date!
author: String!
comment: String!
}`;
9 changes: 9 additions & 0 deletions src/api/type-defs/objects/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ export default `
deployments: [Deployment!]!
}

type TagSchema {
_id: String!
name: String!
default: Boolean!
color: String!
permission: String!
}

type Project {
_id: String!
name: String!
Expand All @@ -24,5 +32,6 @@ export default `
cameraConfigs: [CameraConfig]
labels: LabelList
availableMLModels: [String]
tags: [TagSchema]
}
`;
4 changes: 4 additions & 0 deletions src/api/type-defs/payloads/ImageCommentPayload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default `
type ImageCommentPayload {
comment: ImageComment
}`;
4 changes: 4 additions & 0 deletions src/api/type-defs/root/Mutation.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ export default `
createImage(input: CreateImageInput!): CreateImagePayload
deleteImages(input: DeleteImagesInput!): StandardErrorPayload

createImageComment(input: CreateImageCommentInput!): ImageCommentPayload
updateImageComment(input: UpdateImageCommentInput!): ImageCommentPayload
deleteImageComment(input: DeleteImageCommentInput!): StandardPayload

createUser(input: CreateUserInput!): StandardPayload
updateUser(input: UpdateUserInput!): StandardPayload

Expand Down
Loading