mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Add createGroup API
This commit is contained in:
parent
7f04a381a1
commit
7285ebb940
9 changed files with 229 additions and 6 deletions
|
|
@ -13,7 +13,7 @@ import { User } from '../user'
|
|||
@Entity()
|
||||
export class Group {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id?: string
|
||||
id!: string
|
||||
|
||||
@Column('text')
|
||||
name!: string
|
||||
|
|
@ -23,8 +23,8 @@ export class Group {
|
|||
createdBy!: User
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt?: Date
|
||||
createdAt!: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt?: Date
|
||||
updatedAt!: Date
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,4 +33,7 @@ export class Profile {
|
|||
|
||||
@UpdateDateColumn()
|
||||
updatedAt!: Date
|
||||
|
||||
@Column('boolean', { default: false })
|
||||
private!: boolean
|
||||
}
|
||||
|
|
|
|||
|
|
@ -275,6 +275,29 @@ export type CreateArticleSuccess = {
|
|||
user: User;
|
||||
};
|
||||
|
||||
export type CreateGroupError = {
|
||||
__typename?: 'CreateGroupError';
|
||||
errorCodes: Array<CreateGroupErrorCode>;
|
||||
};
|
||||
|
||||
export enum CreateGroupErrorCode {
|
||||
BadRequest = 'BAD_REQUEST',
|
||||
Unauthorized = 'UNAUTHORIZED'
|
||||
}
|
||||
|
||||
export type CreateGroupInput = {
|
||||
expiresInDays?: InputMaybe<Scalars['Int']>;
|
||||
maxMembers?: InputMaybe<Scalars['Int']>;
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
||||
export type CreateGroupResult = CreateGroupError | CreateGroupSuccess;
|
||||
|
||||
export type CreateGroupSuccess = {
|
||||
__typename?: 'CreateGroupSuccess';
|
||||
group: RecommendationGroup;
|
||||
};
|
||||
|
||||
export type CreateHighlightError = {
|
||||
__typename?: 'CreateHighlightError';
|
||||
errorCodes: Array<CreateHighlightErrorCode>;
|
||||
|
|
@ -1066,6 +1089,7 @@ export type Mutation = {
|
|||
addPopularRead: AddPopularReadResult;
|
||||
createArticle: CreateArticleResult;
|
||||
createArticleSavingRequest: CreateArticleSavingRequestResult;
|
||||
createGroup: CreateGroupResult;
|
||||
createHighlight: CreateHighlightResult;
|
||||
createHighlightReply: CreateHighlightReplyResult;
|
||||
createLabel: CreateLabelResult;
|
||||
|
|
@ -1140,6 +1164,11 @@ export type MutationCreateArticleSavingRequestArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type MutationCreateGroupArgs = {
|
||||
input: CreateGroupInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateHighlightArgs = {
|
||||
input: CreateHighlightInput;
|
||||
};
|
||||
|
|
@ -1696,6 +1725,17 @@ export type RecentSearchesSuccess = {
|
|||
searches: Array<RecentSearch>;
|
||||
};
|
||||
|
||||
export type RecommendationGroup = {
|
||||
__typename?: 'RecommendationGroup';
|
||||
admins: Array<User>;
|
||||
createdAt: Scalars['Date'];
|
||||
id: Scalars['ID'];
|
||||
inviteUrl: Scalars['String'];
|
||||
members: Array<User>;
|
||||
name: Scalars['String'];
|
||||
updatedAt: Scalars['Date'];
|
||||
};
|
||||
|
||||
export type Reminder = {
|
||||
__typename?: 'Reminder';
|
||||
archiveUntil: Scalars['Boolean'];
|
||||
|
|
@ -2909,6 +2949,11 @@ export type ResolversTypes = {
|
|||
CreateArticleSavingRequestResult: ResolversTypes['CreateArticleSavingRequestError'] | ResolversTypes['CreateArticleSavingRequestSuccess'];
|
||||
CreateArticleSavingRequestSuccess: ResolverTypeWrapper<CreateArticleSavingRequestSuccess>;
|
||||
CreateArticleSuccess: ResolverTypeWrapper<CreateArticleSuccess>;
|
||||
CreateGroupError: ResolverTypeWrapper<CreateGroupError>;
|
||||
CreateGroupErrorCode: CreateGroupErrorCode;
|
||||
CreateGroupInput: CreateGroupInput;
|
||||
CreateGroupResult: ResolversTypes['CreateGroupError'] | ResolversTypes['CreateGroupSuccess'];
|
||||
CreateGroupSuccess: ResolverTypeWrapper<CreateGroupSuccess>;
|
||||
CreateHighlightError: ResolverTypeWrapper<CreateHighlightError>;
|
||||
CreateHighlightErrorCode: CreateHighlightErrorCode;
|
||||
CreateHighlightInput: CreateHighlightInput;
|
||||
|
|
@ -3090,6 +3135,7 @@ export type ResolversTypes = {
|
|||
RecentSearchesErrorCode: RecentSearchesErrorCode;
|
||||
RecentSearchesResult: ResolversTypes['RecentSearchesError'] | ResolversTypes['RecentSearchesSuccess'];
|
||||
RecentSearchesSuccess: ResolverTypeWrapper<RecentSearchesSuccess>;
|
||||
RecommendationGroup: ResolverTypeWrapper<RecommendationGroup>;
|
||||
Reminder: ResolverTypeWrapper<Reminder>;
|
||||
ReminderError: ResolverTypeWrapper<ReminderError>;
|
||||
ReminderErrorCode: ReminderErrorCode;
|
||||
|
|
@ -3331,6 +3377,10 @@ export type ResolversParentTypes = {
|
|||
CreateArticleSavingRequestResult: ResolversParentTypes['CreateArticleSavingRequestError'] | ResolversParentTypes['CreateArticleSavingRequestSuccess'];
|
||||
CreateArticleSavingRequestSuccess: CreateArticleSavingRequestSuccess;
|
||||
CreateArticleSuccess: CreateArticleSuccess;
|
||||
CreateGroupError: CreateGroupError;
|
||||
CreateGroupInput: CreateGroupInput;
|
||||
CreateGroupResult: ResolversParentTypes['CreateGroupError'] | ResolversParentTypes['CreateGroupSuccess'];
|
||||
CreateGroupSuccess: CreateGroupSuccess;
|
||||
CreateHighlightError: CreateHighlightError;
|
||||
CreateHighlightInput: CreateHighlightInput;
|
||||
CreateHighlightReplyError: CreateHighlightReplyError;
|
||||
|
|
@ -3475,6 +3525,7 @@ export type ResolversParentTypes = {
|
|||
RecentSearchesError: RecentSearchesError;
|
||||
RecentSearchesResult: ResolversParentTypes['RecentSearchesError'] | ResolversParentTypes['RecentSearchesSuccess'];
|
||||
RecentSearchesSuccess: RecentSearchesSuccess;
|
||||
RecommendationGroup: RecommendationGroup;
|
||||
Reminder: Reminder;
|
||||
ReminderError: ReminderError;
|
||||
ReminderResult: ResolversParentTypes['ReminderError'] | ResolversParentTypes['ReminderSuccess'];
|
||||
|
|
@ -3830,6 +3881,20 @@ export type CreateArticleSuccessResolvers<ContextType = ResolverContext, ParentT
|
|||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type CreateGroupErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateGroupError'] = ResolversParentTypes['CreateGroupError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['CreateGroupErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type CreateGroupResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateGroupResult'] = ResolversParentTypes['CreateGroupResult']> = {
|
||||
__resolveType: TypeResolveFn<'CreateGroupError' | 'CreateGroupSuccess', ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type CreateGroupSuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateGroupSuccess'] = ResolversParentTypes['CreateGroupSuccess']> = {
|
||||
group?: Resolver<ResolversTypes['RecommendationGroup'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type CreateHighlightErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateHighlightError'] = ResolversParentTypes['CreateHighlightError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['CreateHighlightErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
|
|
@ -4417,6 +4482,7 @@ export type MutationResolvers<ContextType = ResolverContext, ParentType extends
|
|||
addPopularRead?: Resolver<ResolversTypes['AddPopularReadResult'], ParentType, ContextType, RequireFields<MutationAddPopularReadArgs, 'name'>>;
|
||||
createArticle?: Resolver<ResolversTypes['CreateArticleResult'], ParentType, ContextType, RequireFields<MutationCreateArticleArgs, 'input'>>;
|
||||
createArticleSavingRequest?: Resolver<ResolversTypes['CreateArticleSavingRequestResult'], ParentType, ContextType, RequireFields<MutationCreateArticleSavingRequestArgs, 'input'>>;
|
||||
createGroup?: Resolver<ResolversTypes['CreateGroupResult'], ParentType, ContextType, RequireFields<MutationCreateGroupArgs, 'input'>>;
|
||||
createHighlight?: Resolver<ResolversTypes['CreateHighlightResult'], ParentType, ContextType, RequireFields<MutationCreateHighlightArgs, 'input'>>;
|
||||
createHighlightReply?: Resolver<ResolversTypes['CreateHighlightReplyResult'], ParentType, ContextType, RequireFields<MutationCreateHighlightReplyArgs, 'input'>>;
|
||||
createLabel?: Resolver<ResolversTypes['CreateLabelResult'], ParentType, ContextType, RequireFields<MutationCreateLabelArgs, 'input'>>;
|
||||
|
|
@ -4616,6 +4682,17 @@ export type RecentSearchesSuccessResolvers<ContextType = ResolverContext, Parent
|
|||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type RecommendationGroupResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['RecommendationGroup'] = ResolversParentTypes['RecommendationGroup']> = {
|
||||
admins?: Resolver<Array<ResolversTypes['User']>, ParentType, ContextType>;
|
||||
createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
inviteUrl?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
members?: Resolver<Array<ResolversTypes['User']>, ParentType, ContextType>;
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type ReminderResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['Reminder'] = ResolversParentTypes['Reminder']> = {
|
||||
archiveUntil?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
|
|
@ -5341,6 +5418,9 @@ export type Resolvers<ContextType = ResolverContext> = {
|
|||
CreateArticleSavingRequestResult?: CreateArticleSavingRequestResultResolvers<ContextType>;
|
||||
CreateArticleSavingRequestSuccess?: CreateArticleSavingRequestSuccessResolvers<ContextType>;
|
||||
CreateArticleSuccess?: CreateArticleSuccessResolvers<ContextType>;
|
||||
CreateGroupError?: CreateGroupErrorResolvers<ContextType>;
|
||||
CreateGroupResult?: CreateGroupResultResolvers<ContextType>;
|
||||
CreateGroupSuccess?: CreateGroupSuccessResolvers<ContextType>;
|
||||
CreateHighlightError?: CreateHighlightErrorResolvers<ContextType>;
|
||||
CreateHighlightReplyError?: CreateHighlightReplyErrorResolvers<ContextType>;
|
||||
CreateHighlightReplyResult?: CreateHighlightReplyResultResolvers<ContextType>;
|
||||
|
|
@ -5468,6 +5548,7 @@ export type Resolvers<ContextType = ResolverContext> = {
|
|||
RecentSearchesError?: RecentSearchesErrorResolvers<ContextType>;
|
||||
RecentSearchesResult?: RecentSearchesResultResolvers<ContextType>;
|
||||
RecentSearchesSuccess?: RecentSearchesSuccessResolvers<ContextType>;
|
||||
RecommendationGroup?: RecommendationGroupResolvers<ContextType>;
|
||||
Reminder?: ReminderResolvers<ContextType>;
|
||||
ReminderError?: ReminderErrorResolvers<ContextType>;
|
||||
ReminderResult?: ReminderResultResolvers<ContextType>;
|
||||
|
|
|
|||
|
|
@ -232,6 +232,27 @@ type CreateArticleSuccess {
|
|||
user: User!
|
||||
}
|
||||
|
||||
type CreateGroupError {
|
||||
errorCodes: [CreateGroupErrorCode!]!
|
||||
}
|
||||
|
||||
enum CreateGroupErrorCode {
|
||||
BAD_REQUEST
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
input CreateGroupInput {
|
||||
expiresInDays: Int
|
||||
maxMembers: Int
|
||||
name: String!
|
||||
}
|
||||
|
||||
union CreateGroupResult = CreateGroupError | CreateGroupSuccess
|
||||
|
||||
type CreateGroupSuccess {
|
||||
group: RecommendationGroup!
|
||||
}
|
||||
|
||||
type CreateHighlightError {
|
||||
errorCodes: [CreateHighlightErrorCode!]!
|
||||
}
|
||||
|
|
@ -948,6 +969,7 @@ type Mutation {
|
|||
addPopularRead(name: String!): AddPopularReadResult!
|
||||
createArticle(input: CreateArticleInput!): CreateArticleResult!
|
||||
createArticleSavingRequest(input: CreateArticleSavingRequestInput!): CreateArticleSavingRequestResult!
|
||||
createGroup(input: CreateGroupInput!): CreateGroupResult!
|
||||
createHighlight(input: CreateHighlightInput!): CreateHighlightResult!
|
||||
createHighlightReply(input: CreateHighlightReplyInput!): CreateHighlightReplyResult!
|
||||
createLabel(input: CreateLabelInput!): CreateLabelResult!
|
||||
|
|
@ -1181,6 +1203,16 @@ type RecentSearchesSuccess {
|
|||
searches: [RecentSearch!]!
|
||||
}
|
||||
|
||||
type RecommendationGroup {
|
||||
admins: [User!]!
|
||||
createdAt: Date!
|
||||
id: ID!
|
||||
inviteUrl: String!
|
||||
members: [User!]!
|
||||
name: String!
|
||||
updatedAt: Date!
|
||||
}
|
||||
|
||||
type Reminder {
|
||||
archiveUntil: Boolean!
|
||||
id: ID!
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
articleSavingRequestResolver,
|
||||
createArticleResolver,
|
||||
createArticleSavingRequestResolver,
|
||||
createGroupResolver,
|
||||
createHighlightResolver,
|
||||
createLabelResolver,
|
||||
createNewsletterEmailResolver,
|
||||
|
|
@ -186,6 +187,7 @@ export const functionResolvers = {
|
|||
saveFilter: saveFilterResolver,
|
||||
deleteFilter: deleteFilterResolver,
|
||||
moveFilter: moveFilterResolver,
|
||||
createGroup: createGroupResolver,
|
||||
},
|
||||
Query: {
|
||||
me: getMeUserResolver,
|
||||
|
|
@ -634,4 +636,5 @@ export const functionResolvers = {
|
|||
...resultResolveTypeResolver('Filters'),
|
||||
...resultResolveTypeResolver('DeleteFilter'),
|
||||
...resultResolveTypeResolver('MoveFilter'),
|
||||
...resultResolveTypeResolver('CreateGroup'),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,3 +23,4 @@ export * from './api_key'
|
|||
export * from './integrations'
|
||||
export * from './rules'
|
||||
export * from './filters'
|
||||
export * from './recommendations'
|
||||
|
|
|
|||
70
packages/api/src/resolvers/recommendations/index.ts
Normal file
70
packages/api/src/resolvers/recommendations/index.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import {
|
||||
CreateGroupError,
|
||||
CreateGroupErrorCode,
|
||||
CreateGroupSuccess,
|
||||
MutationCreateGroupArgs,
|
||||
} from '../../generated/graphql'
|
||||
import { createGroup } from '../../services/create_group'
|
||||
import { authorized, userDataToUser } from '../../utils/helpers'
|
||||
import { homePageURL } from '../../env'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { User } from '../../entity/user'
|
||||
|
||||
export const createGroupResolver = authorized<
|
||||
CreateGroupSuccess,
|
||||
CreateGroupError,
|
||||
MutationCreateGroupArgs
|
||||
>(async (_, { input }, { claims: { uid }, log }) => {
|
||||
log.info('Creating group', {
|
||||
input,
|
||||
labels: {
|
||||
source: 'resolver',
|
||||
resolver: 'createGroupResolver',
|
||||
uid,
|
||||
},
|
||||
})
|
||||
|
||||
try {
|
||||
const userData = await getRepository(User).findOne({
|
||||
where: { id: uid },
|
||||
relations: ['profile'],
|
||||
})
|
||||
if (!userData) {
|
||||
return {
|
||||
errorCodes: [CreateGroupErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
|
||||
const [group, invite] = await createGroup({
|
||||
admin: userData,
|
||||
name: input.name,
|
||||
maxMembers: input.maxMembers,
|
||||
expiresInDays: input.expiresInDays,
|
||||
})
|
||||
|
||||
const inviteUrl = `${homePageURL()}/invite/${invite.code}`
|
||||
const user = userDataToUser(userData)
|
||||
|
||||
return {
|
||||
group: {
|
||||
...group,
|
||||
inviteUrl,
|
||||
admins: [user],
|
||||
members: [user],
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('Error creating group', {
|
||||
error,
|
||||
labels: {
|
||||
source: 'resolver',
|
||||
resolver: 'createGroupResolver',
|
||||
uid,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
errorCodes: [CreateGroupErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -2137,6 +2137,37 @@ const schema = gql`
|
|||
NOT_FOUND
|
||||
}
|
||||
|
||||
input CreateGroupInput {
|
||||
name: String! @sanitize(maxLength: 140)
|
||||
maxMembers: Int
|
||||
expiresInDays: Int
|
||||
}
|
||||
|
||||
union CreateGroupResult = CreateGroupSuccess | CreateGroupError
|
||||
|
||||
type CreateGroupSuccess {
|
||||
group: RecommendationGroup!
|
||||
}
|
||||
|
||||
type RecommendationGroup {
|
||||
id: ID!
|
||||
name: String!
|
||||
inviteUrl: String!
|
||||
admins: [User!]!
|
||||
members: [User!]!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
}
|
||||
|
||||
type CreateGroupError {
|
||||
errorCodes: [CreateGroupErrorCode!]!
|
||||
}
|
||||
|
||||
enum CreateGroupErrorCode {
|
||||
UNAUTHORIZED
|
||||
BAD_REQUEST
|
||||
}
|
||||
|
||||
# Mutations
|
||||
type Mutation {
|
||||
googleLogin(input: GoogleLoginInput!): LoginResult!
|
||||
|
|
@ -2214,6 +2245,7 @@ const schema = gql`
|
|||
saveFilter(input: SaveFilterInput!): SaveFilterResult!
|
||||
deleteFilter(id: ID!): DeleteFilterResult!
|
||||
moveFilter(input: MoveFilterInput!): MoveFilterResult!
|
||||
createGroup(input: CreateGroupInput!): CreateGroupResult!
|
||||
}
|
||||
|
||||
# FIXME: remove sort from feedArticles after all cached tabs are closed
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import { AppDataSource } from '../server'
|
|||
export const createGroup = async (input: {
|
||||
admin: User
|
||||
name: string
|
||||
maxMembers?: number
|
||||
expiresInDays?: number
|
||||
maxMembers?: number | null
|
||||
expiresInDays?: number | null
|
||||
}): Promise<[Group, Invite]> => {
|
||||
const [group, invite] = await AppDataSource.transaction<[Group, Invite]>(
|
||||
async (t) => {
|
||||
|
|
@ -28,7 +28,7 @@ export const createGroup = async (input: {
|
|||
group,
|
||||
code,
|
||||
createdBy: input.admin,
|
||||
maxMembers: input.maxMembers || 50,
|
||||
maxMembers: input.maxMembers || 12,
|
||||
expirationTime: expirationTime,
|
||||
})
|
||||
// Add the admin to the group as its first user
|
||||
|
|
@ -36,6 +36,7 @@ export const createGroup = async (input: {
|
|||
user: input.admin,
|
||||
group,
|
||||
invite,
|
||||
isAdmin: true,
|
||||
})
|
||||
return [group, invite]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue