feat: completed addition of new data field in TeamCollection
This commit is contained in:
@@ -111,7 +111,7 @@ services:
|
|||||||
build:
|
build:
|
||||||
dockerfile: packages/hoppscotch-backend/Dockerfile
|
dockerfile: packages/hoppscotch-backend/Dockerfile
|
||||||
context: .
|
context: .
|
||||||
target: prod
|
target: dev
|
||||||
env_file:
|
env_file:
|
||||||
- ./.env
|
- ./.env
|
||||||
restart: always
|
restart: always
|
||||||
@@ -121,7 +121,7 @@ services:
|
|||||||
- PORT=3000
|
- PORT=3000
|
||||||
volumes:
|
volumes:
|
||||||
# Uncomment the line below when modifying code. Only applicable when using the "dev" target.
|
# Uncomment the line below when modifying code. Only applicable when using the "dev" target.
|
||||||
# - ./packages/hoppscotch-backend/:/usr/src/app
|
- ./packages/hoppscotch-backend/:/usr/src/app
|
||||||
- /usr/src/app/node_modules/
|
- /usr/src/app/node_modules/
|
||||||
depends_on:
|
depends_on:
|
||||||
hoppscotch-db:
|
hoppscotch-db:
|
||||||
|
|||||||
@@ -254,6 +254,13 @@ export const TEAM_COLL_INVALID_JSON = 'team_coll/invalid_json';
|
|||||||
*/
|
*/
|
||||||
export const TEAM_NOT_OWNER = 'team_coll/team_not_owner' as const;
|
export const TEAM_NOT_OWNER = 'team_coll/team_not_owner' as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Team Collection data is not valid
|
||||||
|
* (TeamCollectionService)
|
||||||
|
*/
|
||||||
|
export const TEAM_COLL_DATA_INVALID =
|
||||||
|
'team_coll/team_coll_data_invalid' as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tried to perform an action on a request that doesn't accept their member role level
|
* Tried to perform an action on a request that doesn't accept their member role level
|
||||||
* (GqlRequestTeamMemberGuard)
|
* (GqlRequestTeamMemberGuard)
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ export class CreateRootTeamCollectionArgs {
|
|||||||
|
|
||||||
@Field({ name: 'title', description: 'Title of the new collection' })
|
@Field({ name: 'title', description: 'Title of the new collection' })
|
||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
name: 'data',
|
||||||
|
description: 'JSON string representing the collection data',
|
||||||
|
})
|
||||||
|
data: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ArgsType()
|
@ArgsType()
|
||||||
@@ -26,6 +32,12 @@ export class CreateChildTeamCollectionArgs {
|
|||||||
|
|
||||||
@Field({ name: 'childTitle', description: 'Title of the new collection' })
|
@Field({ name: 'childTitle', description: 'Title of the new collection' })
|
||||||
childTitle: string;
|
childTitle: string;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
name: 'data',
|
||||||
|
description: 'JSON string representing the collection data',
|
||||||
|
})
|
||||||
|
data: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ArgsType()
|
@ArgsType()
|
||||||
@@ -33,12 +45,14 @@ export class RenameTeamCollectionArgs {
|
|||||||
@Field(() => ID, {
|
@Field(() => ID, {
|
||||||
name: 'collectionID',
|
name: 'collectionID',
|
||||||
description: 'ID of the collection',
|
description: 'ID of the collection',
|
||||||
|
deprecationReason: 'Switch to updateTeamCollection mutation instead',
|
||||||
})
|
})
|
||||||
collectionID: string;
|
collectionID: string;
|
||||||
|
|
||||||
@Field({
|
@Field({
|
||||||
name: 'newTitle',
|
name: 'newTitle',
|
||||||
description: 'The updated title of the collection',
|
description: 'The updated title of the collection',
|
||||||
|
deprecationReason: 'Switch to updateTeamCollection mutation instead',
|
||||||
})
|
})
|
||||||
newTitle: string;
|
newTitle: string;
|
||||||
}
|
}
|
||||||
@@ -98,3 +112,25 @@ export class ReplaceTeamCollectionArgs {
|
|||||||
})
|
})
|
||||||
parentCollectionID?: string;
|
parentCollectionID?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ArgsType()
|
||||||
|
export class UpdateTeamCollectionArgs {
|
||||||
|
@Field(() => ID, {
|
||||||
|
name: 'collectionID',
|
||||||
|
description: 'ID of the collection',
|
||||||
|
})
|
||||||
|
collectionID: string;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
name: 'newTitle',
|
||||||
|
description: 'The updated title of the collection',
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
newTitle: string;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
name: 'data',
|
||||||
|
description: 'JSON string representing the collection data',
|
||||||
|
})
|
||||||
|
data: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ export class TeamCollection {
|
|||||||
})
|
})
|
||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
description: 'JSON string representing the collection data',
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
data: string;
|
||||||
|
|
||||||
@Field(() => ID, {
|
@Field(() => ID, {
|
||||||
description: 'ID of the collection',
|
description: 'ID of the collection',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
MoveTeamCollectionArgs,
|
MoveTeamCollectionArgs,
|
||||||
RenameTeamCollectionArgs,
|
RenameTeamCollectionArgs,
|
||||||
ReplaceTeamCollectionArgs,
|
ReplaceTeamCollectionArgs,
|
||||||
|
UpdateTeamCollectionArgs,
|
||||||
UpdateTeamCollectionOrderArgs,
|
UpdateTeamCollectionOrderArgs,
|
||||||
} from './input-type.args';
|
} from './input-type.args';
|
||||||
import * as E from 'fp-ts/Either';
|
import * as E from 'fp-ts/Either';
|
||||||
@@ -141,7 +142,14 @@ export class TeamCollectionResolver {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (E.isLeft(teamCollections)) throwErr(teamCollections.left);
|
if (E.isLeft(teamCollections)) throwErr(teamCollections.left);
|
||||||
return teamCollections.right;
|
return <TeamCollection>{
|
||||||
|
id: teamCollections.right.id,
|
||||||
|
title: teamCollections.right.title,
|
||||||
|
parentID: teamCollections.right.parentID,
|
||||||
|
data: !teamCollections.right.data
|
||||||
|
? null
|
||||||
|
: JSON.stringify(teamCollections.right.data),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mutations
|
// Mutations
|
||||||
@@ -155,6 +163,7 @@ export class TeamCollectionResolver {
|
|||||||
const teamCollection = await this.teamCollectionService.createCollection(
|
const teamCollection = await this.teamCollectionService.createCollection(
|
||||||
args.teamID,
|
args.teamID,
|
||||||
args.title,
|
args.title,
|
||||||
|
args.data,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -230,6 +239,7 @@ export class TeamCollectionResolver {
|
|||||||
const teamCollection = await this.teamCollectionService.createCollection(
|
const teamCollection = await this.teamCollectionService.createCollection(
|
||||||
team.right.id,
|
team.right.id,
|
||||||
args.childTitle,
|
args.childTitle,
|
||||||
|
args.data,
|
||||||
args.collectionID,
|
args.collectionID,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -239,6 +249,7 @@ export class TeamCollectionResolver {
|
|||||||
|
|
||||||
@Mutation(() => TeamCollection, {
|
@Mutation(() => TeamCollection, {
|
||||||
description: 'Rename a collection',
|
description: 'Rename a collection',
|
||||||
|
deprecationReason: 'Switch to updateTeamCollection mutation instead',
|
||||||
})
|
})
|
||||||
@UseGuards(GqlAuthGuard, GqlCollectionTeamMemberGuard)
|
@UseGuards(GqlAuthGuard, GqlCollectionTeamMemberGuard)
|
||||||
@RequiresTeamRole(TeamMemberRole.OWNER, TeamMemberRole.EDITOR)
|
@RequiresTeamRole(TeamMemberRole.OWNER, TeamMemberRole.EDITOR)
|
||||||
@@ -303,6 +314,23 @@ export class TeamCollectionResolver {
|
|||||||
return request.right;
|
return request.right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mutation(() => TeamCollection, {
|
||||||
|
description: 'Update Team Collection details',
|
||||||
|
})
|
||||||
|
@UseGuards(GqlAuthGuard, GqlCollectionTeamMemberGuard)
|
||||||
|
@RequiresTeamRole(TeamMemberRole.OWNER, TeamMemberRole.EDITOR)
|
||||||
|
async updateTeamCollection(@Args() args: UpdateTeamCollectionArgs) {
|
||||||
|
const updatedTeamCollection =
|
||||||
|
await this.teamCollectionService.updateTeamCollection(
|
||||||
|
args.collectionID,
|
||||||
|
args.data,
|
||||||
|
args.newTitle,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (E.isLeft(updatedTeamCollection)) throwErr(updatedTeamCollection.left);
|
||||||
|
return updatedTeamCollection.right;
|
||||||
|
}
|
||||||
|
|
||||||
// Subscriptions
|
// Subscriptions
|
||||||
|
|
||||||
@Subscription(() => TeamCollection, {
|
@Subscription(() => TeamCollection, {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
TEAM_COLL_IS_PARENT_COLL,
|
TEAM_COLL_IS_PARENT_COLL,
|
||||||
TEAM_COL_SAME_NEXT_COLL,
|
TEAM_COL_SAME_NEXT_COLL,
|
||||||
TEAM_COL_REORDERING_FAILED,
|
TEAM_COL_REORDERING_FAILED,
|
||||||
|
TEAM_COLL_DATA_INVALID,
|
||||||
} from '../errors';
|
} from '../errors';
|
||||||
import { PubSubService } from '../pubsub/pubsub.service';
|
import { PubSubService } from '../pubsub/pubsub.service';
|
||||||
import { isValidLength } from 'src/utils';
|
import { isValidLength } from 'src/utils';
|
||||||
@@ -69,6 +70,7 @@ export class TeamCollectionService {
|
|||||||
this.generatePrismaQueryObjForFBCollFolder(f, teamID, index + 1),
|
this.generatePrismaQueryObjForFBCollFolder(f, teamID, index + 1),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
data: folder.data ?? undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +120,7 @@ export class TeamCollectionService {
|
|||||||
name: collection.right.title,
|
name: collection.right.title,
|
||||||
folders: childrenCollectionObjects,
|
folders: childrenCollectionObjects,
|
||||||
requests: requests.map((x) => x.request),
|
requests: requests.map((x) => x.request),
|
||||||
|
data: JSON.stringify(collection.right.data),
|
||||||
};
|
};
|
||||||
|
|
||||||
return E.right(result);
|
return E.right(result);
|
||||||
@@ -199,7 +202,7 @@ export class TeamCollectionService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
teamCollections.forEach((x) =>
|
teamCollections.forEach((x) =>
|
||||||
this.pubsub.publish(`team_coll/${destTeamID}/coll_added`, x),
|
this.pubsub.publish(`team_coll/${destTeamID}/coll_added`, this.cast(x)),
|
||||||
);
|
);
|
||||||
|
|
||||||
return E.right(true);
|
return E.right(true);
|
||||||
@@ -269,7 +272,7 @@ export class TeamCollectionService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
teamCollections.forEach((x) =>
|
teamCollections.forEach((x) =>
|
||||||
this.pubsub.publish(`team_coll/${destTeamID}/coll_added`, x),
|
this.pubsub.publish(`team_coll/${destTeamID}/coll_added`, this.cast(x)),
|
||||||
);
|
);
|
||||||
|
|
||||||
return E.right(true);
|
return E.right(true);
|
||||||
@@ -277,11 +280,17 @@ export class TeamCollectionService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Typecast a database TeamCollection to a TeamCollection model
|
* Typecast a database TeamCollection to a TeamCollection model
|
||||||
|
*
|
||||||
* @param teamCollection database TeamCollection
|
* @param teamCollection database TeamCollection
|
||||||
* @returns TeamCollection model
|
* @returns TeamCollection model
|
||||||
*/
|
*/
|
||||||
private cast(teamCollection: DBTeamCollection): TeamCollection {
|
private cast(teamCollection: DBTeamCollection): TeamCollection {
|
||||||
return <TeamCollection>{ ...teamCollection };
|
return <TeamCollection>{
|
||||||
|
id: teamCollection.id,
|
||||||
|
title: teamCollection.title,
|
||||||
|
parentID: teamCollection.parentID,
|
||||||
|
data: !teamCollection.data ? null : JSON.stringify(teamCollection.data),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -324,7 +333,7 @@ export class TeamCollectionService {
|
|||||||
});
|
});
|
||||||
if (!teamCollection) return null;
|
if (!teamCollection) return null;
|
||||||
|
|
||||||
return teamCollection.parent;
|
return !teamCollection.parent ? null : this.cast(teamCollection.parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -335,12 +344,12 @@ export class TeamCollectionService {
|
|||||||
* @param take Number of items we want returned
|
* @param take Number of items we want returned
|
||||||
* @returns A list of child collections
|
* @returns A list of child collections
|
||||||
*/
|
*/
|
||||||
getChildrenOfCollection(
|
async getChildrenOfCollection(
|
||||||
collectionID: string,
|
collectionID: string,
|
||||||
cursor: string | null,
|
cursor: string | null,
|
||||||
take: number,
|
take: number,
|
||||||
) {
|
) {
|
||||||
return this.prisma.teamCollection.findMany({
|
const res = await this.prisma.teamCollection.findMany({
|
||||||
where: {
|
where: {
|
||||||
parentID: collectionID,
|
parentID: collectionID,
|
||||||
},
|
},
|
||||||
@@ -351,6 +360,12 @@ export class TeamCollectionService {
|
|||||||
skip: cursor ? 1 : 0,
|
skip: cursor ? 1 : 0,
|
||||||
cursor: cursor ? { id: cursor } : undefined,
|
cursor: cursor ? { id: cursor } : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const childCollections = res.map((teamCollection) =>
|
||||||
|
this.cast(teamCollection),
|
||||||
|
);
|
||||||
|
|
||||||
|
return childCollections;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -366,7 +381,7 @@ export class TeamCollectionService {
|
|||||||
cursor: string | null,
|
cursor: string | null,
|
||||||
take: number,
|
take: number,
|
||||||
) {
|
) {
|
||||||
return this.prisma.teamCollection.findMany({
|
const res = await this.prisma.teamCollection.findMany({
|
||||||
where: {
|
where: {
|
||||||
teamID,
|
teamID,
|
||||||
parentID: null,
|
parentID: null,
|
||||||
@@ -378,6 +393,12 @@ export class TeamCollectionService {
|
|||||||
skip: cursor ? 1 : 0,
|
skip: cursor ? 1 : 0,
|
||||||
cursor: cursor ? { id: cursor } : undefined,
|
cursor: cursor ? { id: cursor } : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const teamCollections = res.map((teamCollection) =>
|
||||||
|
this.cast(teamCollection),
|
||||||
|
);
|
||||||
|
|
||||||
|
return teamCollections;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -470,6 +491,7 @@ export class TeamCollectionService {
|
|||||||
async createCollection(
|
async createCollection(
|
||||||
teamID: string,
|
teamID: string,
|
||||||
title: string,
|
title: string,
|
||||||
|
data: string,
|
||||||
parentTeamCollectionID: string | null,
|
parentTeamCollectionID: string | null,
|
||||||
) {
|
) {
|
||||||
const isTitleValid = isValidLength(title, this.TITLE_LENGTH);
|
const isTitleValid = isValidLength(title, this.TITLE_LENGTH);
|
||||||
@@ -481,6 +503,9 @@ export class TeamCollectionService {
|
|||||||
if (O.isNone(isOwner)) return E.left(TEAM_NOT_OWNER);
|
if (O.isNone(isOwner)) return E.left(TEAM_NOT_OWNER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const collectionData = stringToJson(data);
|
||||||
|
if (E.isLeft(collectionData)) return E.left(TEAM_COLL_DATA_INVALID);
|
||||||
|
|
||||||
const isParent = parentTeamCollectionID
|
const isParent = parentTeamCollectionID
|
||||||
? {
|
? {
|
||||||
connect: {
|
connect: {
|
||||||
@@ -498,18 +523,23 @@ export class TeamCollectionService {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
parent: isParent,
|
parent: isParent,
|
||||||
|
data: collectionData.right,
|
||||||
orderIndex: !parentTeamCollectionID
|
orderIndex: !parentTeamCollectionID
|
||||||
? (await this.getRootCollectionsCount(teamID)) + 1
|
? (await this.getRootCollectionsCount(teamID)) + 1
|
||||||
: (await this.getChildCollectionsCount(parentTeamCollectionID)) + 1,
|
: (await this.getChildCollectionsCount(parentTeamCollectionID)) + 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
this.pubsub.publish(`team_coll/${teamID}/coll_added`, teamCollection);
|
this.pubsub.publish(
|
||||||
|
`team_coll/${teamID}/coll_added`,
|
||||||
|
this.cast(teamCollection),
|
||||||
|
);
|
||||||
|
|
||||||
return E.right(this.cast(teamCollection));
|
return E.right(this.cast(teamCollection));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated Use updateTeamCollection method instead
|
||||||
* Update the title of a TeamCollection
|
* Update the title of a TeamCollection
|
||||||
*
|
*
|
||||||
* @param collectionID The Collection ID
|
* @param collectionID The Collection ID
|
||||||
@@ -532,10 +562,10 @@ export class TeamCollectionService {
|
|||||||
|
|
||||||
this.pubsub.publish(
|
this.pubsub.publish(
|
||||||
`team_coll/${updatedTeamCollection.teamID}/coll_updated`,
|
`team_coll/${updatedTeamCollection.teamID}/coll_updated`,
|
||||||
updatedTeamCollection,
|
this.cast(updatedTeamCollection),
|
||||||
);
|
);
|
||||||
|
|
||||||
return E.right(updatedTeamCollection);
|
return E.right(this.cast(updatedTeamCollection));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return E.left(TEAM_COLL_NOT_FOUND);
|
return E.left(TEAM_COLL_NOT_FOUND);
|
||||||
}
|
}
|
||||||
@@ -694,8 +724,8 @@ export class TeamCollectionService {
|
|||||||
* @returns An Option of boolean, is parent or not
|
* @returns An Option of boolean, is parent or not
|
||||||
*/
|
*/
|
||||||
private async isParent(
|
private async isParent(
|
||||||
collection: TeamCollection,
|
collection: DBTeamCollection,
|
||||||
destCollection: TeamCollection,
|
destCollection: DBTeamCollection,
|
||||||
): Promise<O.Option<boolean>> {
|
): Promise<O.Option<boolean>> {
|
||||||
//* Recursively check if collection is a parent by going up the tree of child-parent collections until we reach a root collection i.e parentID === null
|
//* Recursively check if collection is a parent by going up the tree of child-parent collections until we reach a root collection i.e parentID === null
|
||||||
//* Valid condition, isParent returns false
|
//* Valid condition, isParent returns false
|
||||||
@@ -971,4 +1001,43 @@ export class TeamCollectionService {
|
|||||||
const teamCollectionsCount = this.prisma.teamCollection.count();
|
const teamCollectionsCount = this.prisma.teamCollection.count();
|
||||||
return teamCollectionsCount;
|
return teamCollectionsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Team Collection details
|
||||||
|
*
|
||||||
|
* @param collectionID Collection ID
|
||||||
|
* @param collectionData new header data in a JSONified string form
|
||||||
|
* @param newTitle New title of the collection
|
||||||
|
* @returns Updated TeamCollection
|
||||||
|
*/
|
||||||
|
async updateTeamCollection(
|
||||||
|
collectionID: string,
|
||||||
|
collectionData: string,
|
||||||
|
newTitle: string = null,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
if (collectionData) {
|
||||||
|
const jsonReq = stringToJson(collectionData);
|
||||||
|
if (E.isLeft(jsonReq)) return E.left(TEAM_COLL_DATA_INVALID);
|
||||||
|
collectionData = jsonReq.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedTeamCollection = await this.prisma.teamCollection.update({
|
||||||
|
where: { id: collectionID },
|
||||||
|
data: {
|
||||||
|
data: collectionData,
|
||||||
|
title: newTitle ?? undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.pubsub.publish(
|
||||||
|
`team_coll/${updatedTeamCollection.teamID}/coll_updated`,
|
||||||
|
this.cast(updatedTeamCollection),
|
||||||
|
);
|
||||||
|
|
||||||
|
return E.right(this.cast(updatedTeamCollection));
|
||||||
|
} catch (e) {
|
||||||
|
return E.left(TEAM_COLL_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ export interface CollectionFolder {
|
|||||||
folders: CollectionFolder[];
|
folders: CollectionFolder[];
|
||||||
requests: any[];
|
requests: any[];
|
||||||
name: string;
|
name: string;
|
||||||
|
data?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user