Skip to content

Commit

Permalink
Implement the functionality to create tags for user (PalisadoesFounda…
Browse files Browse the repository at this point in the history
…tion#1158)

* Add userOrgTag schema

* Update tag validation

* Introduce tags and tagFolders in models

* Export the newly added models

* Add createTagFolder

* Add removeTagFolder

* Add updateTagFolder mutation

* Add all tag created CRUD operations

* Add assignTag and unassignTag mutations

* Add usersByTag Query

* Change models and move all migrations

* Add users field on Tag Interface

* Add field resolver to get all tags for a user

* Add resolvers for tag folder structure

* Improve validation and indexing in models

* Update schema and models to remove tagFolder model

* Update Tag field resolvers

* Update field resolvers in User and Org models

* Remove deprecated tagFolder mutation files

* Update assign and unassign mutations, constants

* Rename fields

* Update createTag mutation

* Migrate all error objects to the new style

* Add tests for Tag field resolvers

* Make changes to typedefs and model implementations as per the review

* Change typedefs

* Updated typedefs

* Migrate models to allow assigning to multiple object types

* Update tests for tag/userAssignedTo

* Update pagination

* Update typedefs

* Remove OrganizationTags connection

* Add pagination to usersAssignedTo.ts

* Fix typos

* Update typedefs

* Add Error Type

* Remove resolvers

* Update generated types

* Update comments

* Update typedefs

* Improve comments

* Rename type

* Bugfix
  • Loading branch information
EshaanAgg authored Mar 15, 2023
1 parent 2b6fc80 commit b5da483
Show file tree
Hide file tree
Showing 11 changed files with 408 additions and 3 deletions.
5 changes: 5 additions & 0 deletions codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ const config: CodegenConfig = {

Task: "../models/Task#Interface_Task",

OrganizationTagUser:
"../models/OrganizationTagUser#Interface_OrganizationTagUser",

TagUser: "../models/TagUser#Interface_TagUser",

User: "../models/User#Interface_User",
},

Expand Down
34 changes: 31 additions & 3 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ export const INVALID_FILE_TYPE = {
};

export const SAME_FILE_ERROR = {
message: "The newer image is the same as the previous image in the database",
code: "internalServerError",
param: "internalServerError",
MESSAGE: "The newer image is the same as the previous image in the database",
CODE: "internalServerError",
PARAM: "internalServerError",
};

export const INTERNAL_SERVER_ERROR = {
Expand Down Expand Up @@ -163,6 +163,34 @@ export const USER_NOT_AUTHORIZED_TO_PIN = {
CODE: "user.notAuthorizedToPin",
PARAM: "user.notAuthorizedToPin",
};

export const TAG_NOT_FOUND = {
MESSAGE: "The tag with the specified ID doesn't exist.",
CODE: "tag.doesNotExist",
PARAM: "tag.doesNotExist",
};

export const INVALID_TAG_INPUT = {
MESSAGE:
"Either an organizatin ID or a parent tag ID must be provided for this operation.",
CODE: "invalidArgs",
PARAM: "invalidArgs",
};

export const INCORRECT_TAG_INPUT = {
MESSAGE:
"The tag does not belong to the organization provided. Try sending only one correct PARAMeter.",
CODE: "invalidArgs.tag",
PARAM: "invalidArgs.tag",
};

export const USER_NOT_AUTHORIZED_TO_CREATE_TAG = {
MESSAGE:
"The user must be a superadmin or an admin of the organization to create the tag.",
CODE: "user.notAuth.createTag",
PARAM: "user.notAuth.createTag",
};

export const TASK_NOT_FOUND_ERROR = {
DESC: "Task not found",
CODE: "task.notFound",
Expand Down
45 changes: 45 additions & 0 deletions src/models/OrganizationTagUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Schema, model, PopulatedDoc, Types, Document, models } from "mongoose";
import { Interface_Organization } from "./Organization";

export interface Interface_OrganizationTagUser {
_id: Types.ObjectId;
organizationId: PopulatedDoc<Interface_Organization & Document>;
parentTagId: PopulatedDoc<Interface_OrganizationTagUser & Document>;
name: string;
}

// A User Tag is used for the categorization and the grouping of related users
// Each tag belongs to a particular organization, and is private to the same.
// Each tag can be nested to hold other sub-tags so as to create a heriecheal structure.
const OrganizationTagUserSchema = new Schema({
name: {
type: String,
required: true,
},
organizationId: {
type: Schema.Types.ObjectId,
ref: "Organization",
required: true,
},
parentTagId: {
type: Schema.Types.ObjectId,
ref: "OrganizationTagUser",
required: false,
default: null, // A null parent corresponds to a root tag in the organization
},
});

OrganizationTagUserSchema.index(
{ organizationId: 1, parentOrganizationTagUserId: 1, name: 1 },
{ unique: true }
);

const OrganizationTagUserModel = () =>
model<Interface_OrganizationTagUser>(
"OrganizationTagUser",
OrganizationTagUserSchema
);

// This syntax is needed to prevent Mongoose OverwriteModelError while running tests.
export const OrganizationTagUser = (models.OrganizationTagUser ||
OrganizationTagUserModel()) as ReturnType<typeof OrganizationTagUserModel>;
32 changes: 32 additions & 0 deletions src/models/TagUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Schema, model, PopulatedDoc, Types, Document, models } from "mongoose";
import { Interface_OrganizationTagUser } from "./OrganizationTagUser";
import { Interface_User } from "./User";

export interface Interface_TagUser {
_id: Types.ObjectId;
userId: PopulatedDoc<Interface_User & Document>;
tagId: PopulatedDoc<Interface_OrganizationTagUser & Document>;
}

// Relational schema used to keep track of assigned tags to users
const TagUserSchema = new Schema({
userId: {
type: Schema.Types.ObjectId,
required: true,
ref: "User",
},
tagId: {
type: Schema.Types.ObjectId,
ref: "OrganizationTagUser",
required: true,
},
});

TagUserSchema.index({ userId: 1, tagId: 1 }, { unique: true });

const TagUserModel = () => model<Interface_TagUser>("TagUser", TagUserSchema);

// This syntax is needed to prevent Mongoose OverwriteModelError while running tests.
export const TagUser = (models.TagUser || TagUserModel()) as ReturnType<
typeof TagUserModel
>;
2 changes: 2 additions & 0 deletions src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ export * from "./Language";
export * from "./MembershipRequest";
export * from "./Message";
export * from "./Organization";
export * from "./OrganizationTagUser";
export * from "./Plugin";
export * from "./PluginField";
export * from "./Post";
export * from "./Task";
export * from "./User";
export * from "./TagUser";
2 changes: 2 additions & 0 deletions src/resolvers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
LatitudeResolver,
LongitudeResolver,
PhoneNumberResolver,
PositiveIntResolver,
TimeResolver,
URLResolver,
} from "graphql-scalars";
Expand All @@ -37,6 +38,7 @@ export const resolvers: Resolvers = {
Latitude: LatitudeResolver,
Longitude: LongitudeResolver,
PhoneNumber: PhoneNumberResolver,
PositiveInt: PositiveIntResolver,
Time: TimeResolver,
URL: URLResolver,
};
16 changes: 16 additions & 0 deletions src/typeDefs/inputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ export const inputs = gql`
title: String!
}
input CreateUserTagInput {
name: String!
parentTagId: ID
organizationId: ID!
}
input DonationWhereInput {
id: ID
id_not: ID
Expand Down Expand Up @@ -222,6 +228,11 @@ export const inputs = gql`
deadline: DateTime
}
input ToggleUserTagAssignInput {
userId: ID!
tagId: ID!
}
input UpdateEventInput {
title: String
description: String
Expand Down Expand Up @@ -252,6 +263,11 @@ export const inputs = gql`
location: String
}
type UpdateUserTagInput {
_id: ID!
name: String!
}
input UpdateTaskInput {
title: String
description: String
Expand Down
8 changes: 8 additions & 0 deletions src/typeDefs/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export const mutations = gql`
createPost(data: PostInput!, file: String): Post @auth
createUserTag(input: CreateUserTagInput!): UserTag @auth
createTask(data: TaskInput, eventId: ID!): Task! @auth
deleteDonationById(id: ID!): DeletePayload!
Expand Down Expand Up @@ -117,6 +119,8 @@ export const mutations = gql`
removePost(id: ID!): Post @auth
removeUserTag(id: ID!): UserTag @auth
removeTask(id: ID!): Task @auth
removeUserFromGroupChat(userId: ID!, chatId: ID!): GroupChat! @auth
Expand All @@ -143,6 +147,8 @@ export const mutations = gql`
togglePostPin(id: ID!): Post! @auth
toggleUserTagAssign(input: ToggleUserTagAssignInput!): User @auth
unblockUser(organizationId: ID!, userId: ID!): User! @auth
unlikeComment(id: ID!): Comment @auth
Expand All @@ -164,6 +170,8 @@ export const mutations = gql`
updatePluginStatus(id: ID!, status: Boolean!): Plugin!
updateUserTag(input: UpdateUserTagInput!): UserTag @auth
updateTask(id: ID!, data: UpdateTaskInput): Task @auth
updateUserProfile(data: UpdateUserInput, file: String): User! @auth
Expand Down
1 change: 1 addition & 0 deletions src/typeDefs/scalars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const scalars = gql`
scalar Longitude
scalar ID
scalar PhoneNumber
scalar PositiveInt
scalar Time
scalar URL
`;
60 changes: 60 additions & 0 deletions src/typeDefs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ export const types = gql`
likeCount: Int
}
# A page info type adhering to Relay Specification for both cursor based pagination
type ConnectionPageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
type DeletePayload {
success: Boolean!
}
Expand Down Expand Up @@ -199,6 +207,12 @@ export const types = gql`
apiUrl: URL!
createdAt: DateTime
pinnedPosts: [Post]
userTags(
after: String
before: String
first: PositiveInt
last: PositiveInt
): UserTagsConnection
}
type OrganizationInfoNode {
Expand Down Expand Up @@ -229,6 +243,7 @@ export const types = gql`
When paginating backwards, are there more items?
"""
hasPreviousPage: Boolean!
totalPages: Int
nextPageNo: Int
prevPageNo: Int
Expand Down Expand Up @@ -332,6 +347,12 @@ export const types = gql`
pluginCreationAllowed: Boolean
adminApproved: Boolean
createdAt: DateTime
tagsAssignedWith(
after: String
before: String
first: PositiveInt
last: PositiveInt
): UserTagsConnection
}
type UserAttende {
Expand All @@ -347,4 +368,43 @@ export const types = gql`
edges: [User]!
aggregate: AggregateUser!
}
type UserEdge {
node: User!
cursor: String!
}
type UserTag {
_id: ID!
name: String!
organization: Organization
parentTag: UserTag
childTags(
after: String
before: String
first: PositiveInt
last: PositiveInt
): UserTagsConnection
usersAssignedTo(
after: String
before: String
first: PositiveInt
last: PositiveInt
): UsersConnection
}
type UserTagsConnection {
edges: [UserTagEdge]
pageInfo: ConnectionPageInfo!
}
type UserTagEdge {
node: UserTag!
cursor: String!
}
type UsersConnection {
edges: [UserEdge]
pageInfo: ConnectionPageInfo!
}
`;
Loading

0 comments on commit b5da483

Please sign in to comment.