Add createIntegration API implementation

This commit is contained in:
Hongbo Wu 2022-08-01 22:42:55 +08:00
parent e66d047d51
commit dd2db71876
8 changed files with 237 additions and 90 deletions

View file

@ -25,7 +25,7 @@ export class Integration {
@Column('enum', { enum: IntegrationType }) @Column('enum', { enum: IntegrationType })
type!: IntegrationType type!: IntegrationType
@Column('varchar') @Column('varchar', { length: 255 })
token!: string token!: string
@Column('boolean', { default: true }) @Column('boolean', { default: true })

View file

@ -331,30 +331,6 @@ export type CreateHighlightSuccess = {
highlight: Highlight; highlight: Highlight;
}; };
export type CreateIntegrationError = {
__typename?: 'CreateIntegrationError';
errorCodes: Array<CreateIntegrationErrorCode>;
};
export enum CreateIntegrationErrorCode {
BadRequest = 'BAD_REQUEST',
InvalidToken = 'INVALID_TOKEN',
NotFound = 'NOT_FOUND',
Unauthorized = 'UNAUTHORIZED'
}
export type CreateIntegrationInput = {
token: Scalars['String'];
type: IntegrationType;
};
export type CreateIntegrationResult = CreateIntegrationError | CreateIntegrationSuccess;
export type CreateIntegrationSuccess = {
__typename?: 'CreateIntegrationSuccess';
integration: Integration;
};
export type CreateLabelError = { export type CreateLabelError = {
__typename?: 'CreateLabelError'; __typename?: 'CreateLabelError';
errorCodes: Array<CreateLabelErrorCode>; errorCodes: Array<CreateLabelErrorCode>;
@ -774,6 +750,7 @@ export type Integration = {
createdAt: Scalars['Date']; createdAt: Scalars['Date'];
enabled: Scalars['Boolean']; enabled: Scalars['Boolean'];
id: Scalars['ID']; id: Scalars['ID'];
token: Scalars['String'];
type: IntegrationType; type: IntegrationType;
updatedAt: Scalars['Date']; updatedAt: Scalars['Date'];
}; };
@ -934,7 +911,6 @@ export type Mutation = {
createArticleSavingRequest: CreateArticleSavingRequestResult; createArticleSavingRequest: CreateArticleSavingRequestResult;
createHighlight: CreateHighlightResult; createHighlight: CreateHighlightResult;
createHighlightReply: CreateHighlightReplyResult; createHighlightReply: CreateHighlightReplyResult;
createIntegration: CreateIntegrationResult;
createLabel: CreateLabelResult; createLabel: CreateLabelResult;
createNewsletterEmail: CreateNewsletterEmailResult; createNewsletterEmail: CreateNewsletterEmailResult;
createReaction: CreateReactionResult; createReaction: CreateReactionResult;
@ -962,6 +938,7 @@ export type Mutation = {
setBookmarkArticle: SetBookmarkArticleResult; setBookmarkArticle: SetBookmarkArticleResult;
setDeviceToken: SetDeviceTokenResult; setDeviceToken: SetDeviceTokenResult;
setFollow: SetFollowResult; setFollow: SetFollowResult;
setIntegration: SetIntegrationResult;
setLabels: SetLabelsResult; setLabels: SetLabelsResult;
setLabelsForHighlight: SetLabelsResult; setLabelsForHighlight: SetLabelsResult;
setLinkArchived: ArchiveLinkResult; setLinkArchived: ArchiveLinkResult;
@ -1009,11 +986,6 @@ export type MutationCreateHighlightReplyArgs = {
}; };
export type MutationCreateIntegrationArgs = {
input: CreateIntegrationInput;
};
export type MutationCreateLabelArgs = { export type MutationCreateLabelArgs = {
input: CreateLabelInput; input: CreateLabelInput;
}; };
@ -1139,6 +1111,11 @@ export type MutationSetFollowArgs = {
}; };
export type MutationSetIntegrationArgs = {
input: SetIntegrationInput;
};
export type MutationSetLabelsArgs = { export type MutationSetLabelsArgs = {
input: SetLabelsInput; input: SetLabelsInput;
}; };
@ -1737,6 +1714,33 @@ export type SetFollowSuccess = {
updatedUser: User; updatedUser: User;
}; };
export type SetIntegrationError = {
__typename?: 'SetIntegrationError';
errorCodes: Array<SetIntegrationErrorCode>;
};
export enum SetIntegrationErrorCode {
AlreadyExists = 'ALREADY_EXISTS',
BadRequest = 'BAD_REQUEST',
InvalidToken = 'INVALID_TOKEN',
NotFound = 'NOT_FOUND',
Unauthorized = 'UNAUTHORIZED'
}
export type SetIntegrationInput = {
enabled?: InputMaybe<Scalars['Boolean']>;
id?: InputMaybe<Scalars['ID']>;
token: Scalars['String'];
type: IntegrationType;
};
export type SetIntegrationResult = SetIntegrationError | SetIntegrationSuccess;
export type SetIntegrationSuccess = {
__typename?: 'SetIntegrationSuccess';
integration: Integration;
};
export type SetLabelsError = { export type SetLabelsError = {
__typename?: 'SetLabelsError'; __typename?: 'SetLabelsError';
errorCodes: Array<SetLabelsErrorCode>; errorCodes: Array<SetLabelsErrorCode>;
@ -2548,11 +2552,6 @@ export type ResolversTypes = {
CreateHighlightReplySuccess: ResolverTypeWrapper<CreateHighlightReplySuccess>; CreateHighlightReplySuccess: ResolverTypeWrapper<CreateHighlightReplySuccess>;
CreateHighlightResult: ResolversTypes['CreateHighlightError'] | ResolversTypes['CreateHighlightSuccess']; CreateHighlightResult: ResolversTypes['CreateHighlightError'] | ResolversTypes['CreateHighlightSuccess'];
CreateHighlightSuccess: ResolverTypeWrapper<CreateHighlightSuccess>; CreateHighlightSuccess: ResolverTypeWrapper<CreateHighlightSuccess>;
CreateIntegrationError: ResolverTypeWrapper<CreateIntegrationError>;
CreateIntegrationErrorCode: CreateIntegrationErrorCode;
CreateIntegrationInput: CreateIntegrationInput;
CreateIntegrationResult: ResolversTypes['CreateIntegrationError'] | ResolversTypes['CreateIntegrationSuccess'];
CreateIntegrationSuccess: ResolverTypeWrapper<CreateIntegrationSuccess>;
CreateLabelError: ResolverTypeWrapper<CreateLabelError>; CreateLabelError: ResolverTypeWrapper<CreateLabelError>;
CreateLabelErrorCode: CreateLabelErrorCode; CreateLabelErrorCode: CreateLabelErrorCode;
CreateLabelInput: CreateLabelInput; CreateLabelInput: CreateLabelInput;
@ -2732,6 +2731,11 @@ export type ResolversTypes = {
SetFollowInput: SetFollowInput; SetFollowInput: SetFollowInput;
SetFollowResult: ResolversTypes['SetFollowError'] | ResolversTypes['SetFollowSuccess']; SetFollowResult: ResolversTypes['SetFollowError'] | ResolversTypes['SetFollowSuccess'];
SetFollowSuccess: ResolverTypeWrapper<SetFollowSuccess>; SetFollowSuccess: ResolverTypeWrapper<SetFollowSuccess>;
SetIntegrationError: ResolverTypeWrapper<SetIntegrationError>;
SetIntegrationErrorCode: SetIntegrationErrorCode;
SetIntegrationInput: SetIntegrationInput;
SetIntegrationResult: ResolversTypes['SetIntegrationError'] | ResolversTypes['SetIntegrationSuccess'];
SetIntegrationSuccess: ResolverTypeWrapper<SetIntegrationSuccess>;
SetLabelsError: ResolverTypeWrapper<SetLabelsError>; SetLabelsError: ResolverTypeWrapper<SetLabelsError>;
SetLabelsErrorCode: SetLabelsErrorCode; SetLabelsErrorCode: SetLabelsErrorCode;
SetLabelsForHighlightInput: SetLabelsForHighlightInput; SetLabelsForHighlightInput: SetLabelsForHighlightInput;
@ -2909,10 +2913,6 @@ export type ResolversParentTypes = {
CreateHighlightReplySuccess: CreateHighlightReplySuccess; CreateHighlightReplySuccess: CreateHighlightReplySuccess;
CreateHighlightResult: ResolversParentTypes['CreateHighlightError'] | ResolversParentTypes['CreateHighlightSuccess']; CreateHighlightResult: ResolversParentTypes['CreateHighlightError'] | ResolversParentTypes['CreateHighlightSuccess'];
CreateHighlightSuccess: CreateHighlightSuccess; CreateHighlightSuccess: CreateHighlightSuccess;
CreateIntegrationError: CreateIntegrationError;
CreateIntegrationInput: CreateIntegrationInput;
CreateIntegrationResult: ResolversParentTypes['CreateIntegrationError'] | ResolversParentTypes['CreateIntegrationSuccess'];
CreateIntegrationSuccess: CreateIntegrationSuccess;
CreateLabelError: CreateLabelError; CreateLabelError: CreateLabelError;
CreateLabelInput: CreateLabelInput; CreateLabelInput: CreateLabelInput;
CreateLabelResult: ResolversParentTypes['CreateLabelError'] | ResolversParentTypes['CreateLabelSuccess']; CreateLabelResult: ResolversParentTypes['CreateLabelError'] | ResolversParentTypes['CreateLabelSuccess'];
@ -3056,6 +3056,10 @@ export type ResolversParentTypes = {
SetFollowInput: SetFollowInput; SetFollowInput: SetFollowInput;
SetFollowResult: ResolversParentTypes['SetFollowError'] | ResolversParentTypes['SetFollowSuccess']; SetFollowResult: ResolversParentTypes['SetFollowError'] | ResolversParentTypes['SetFollowSuccess'];
SetFollowSuccess: SetFollowSuccess; SetFollowSuccess: SetFollowSuccess;
SetIntegrationError: SetIntegrationError;
SetIntegrationInput: SetIntegrationInput;
SetIntegrationResult: ResolversParentTypes['SetIntegrationError'] | ResolversParentTypes['SetIntegrationSuccess'];
SetIntegrationSuccess: SetIntegrationSuccess;
SetLabelsError: SetLabelsError; SetLabelsError: SetLabelsError;
SetLabelsForHighlightInput: SetLabelsForHighlightInput; SetLabelsForHighlightInput: SetLabelsForHighlightInput;
SetLabelsInput: SetLabelsInput; SetLabelsInput: SetLabelsInput;
@ -3382,20 +3386,6 @@ export type CreateHighlightSuccessResolvers<ContextType = ResolverContext, Paren
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}; };
export type CreateIntegrationErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateIntegrationError'] = ResolversParentTypes['CreateIntegrationError']> = {
errorCodes?: Resolver<Array<ResolversTypes['CreateIntegrationErrorCode']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type CreateIntegrationResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateIntegrationResult'] = ResolversParentTypes['CreateIntegrationResult']> = {
__resolveType: TypeResolveFn<'CreateIntegrationError' | 'CreateIntegrationSuccess', ParentType, ContextType>;
};
export type CreateIntegrationSuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateIntegrationSuccess'] = ResolversParentTypes['CreateIntegrationSuccess']> = {
integration?: Resolver<ResolversTypes['Integration'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type CreateLabelErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateLabelError'] = ResolversParentTypes['CreateLabelError']> = { export type CreateLabelErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateLabelError'] = ResolversParentTypes['CreateLabelError']> = {
errorCodes?: Resolver<Array<ResolversTypes['CreateLabelErrorCode']>, ParentType, ContextType>; errorCodes?: Resolver<Array<ResolversTypes['CreateLabelErrorCode']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
@ -3717,6 +3707,7 @@ export type IntegrationResolvers<ContextType = ResolverContext, ParentType exten
createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>; createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
enabled?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>; enabled?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>; id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
token?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
type?: Resolver<ResolversTypes['IntegrationType'], ParentType, ContextType>; type?: Resolver<ResolversTypes['IntegrationType'], ParentType, ContextType>;
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>; updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
@ -3833,7 +3824,6 @@ export type MutationResolvers<ContextType = ResolverContext, ParentType extends
createArticleSavingRequest?: Resolver<ResolversTypes['CreateArticleSavingRequestResult'], ParentType, ContextType, RequireFields<MutationCreateArticleSavingRequestArgs, 'input'>>; createArticleSavingRequest?: Resolver<ResolversTypes['CreateArticleSavingRequestResult'], ParentType, ContextType, RequireFields<MutationCreateArticleSavingRequestArgs, 'input'>>;
createHighlight?: Resolver<ResolversTypes['CreateHighlightResult'], ParentType, ContextType, RequireFields<MutationCreateHighlightArgs, 'input'>>; createHighlight?: Resolver<ResolversTypes['CreateHighlightResult'], ParentType, ContextType, RequireFields<MutationCreateHighlightArgs, 'input'>>;
createHighlightReply?: Resolver<ResolversTypes['CreateHighlightReplyResult'], ParentType, ContextType, RequireFields<MutationCreateHighlightReplyArgs, 'input'>>; createHighlightReply?: Resolver<ResolversTypes['CreateHighlightReplyResult'], ParentType, ContextType, RequireFields<MutationCreateHighlightReplyArgs, 'input'>>;
createIntegration?: Resolver<ResolversTypes['CreateIntegrationResult'], ParentType, ContextType, RequireFields<MutationCreateIntegrationArgs, 'input'>>;
createLabel?: Resolver<ResolversTypes['CreateLabelResult'], ParentType, ContextType, RequireFields<MutationCreateLabelArgs, 'input'>>; createLabel?: Resolver<ResolversTypes['CreateLabelResult'], ParentType, ContextType, RequireFields<MutationCreateLabelArgs, 'input'>>;
createNewsletterEmail?: Resolver<ResolversTypes['CreateNewsletterEmailResult'], ParentType, ContextType>; createNewsletterEmail?: Resolver<ResolversTypes['CreateNewsletterEmailResult'], ParentType, ContextType>;
createReaction?: Resolver<ResolversTypes['CreateReactionResult'], ParentType, ContextType, RequireFields<MutationCreateReactionArgs, 'input'>>; createReaction?: Resolver<ResolversTypes['CreateReactionResult'], ParentType, ContextType, RequireFields<MutationCreateReactionArgs, 'input'>>;
@ -3861,6 +3851,7 @@ export type MutationResolvers<ContextType = ResolverContext, ParentType extends
setBookmarkArticle?: Resolver<ResolversTypes['SetBookmarkArticleResult'], ParentType, ContextType, RequireFields<MutationSetBookmarkArticleArgs, 'input'>>; setBookmarkArticle?: Resolver<ResolversTypes['SetBookmarkArticleResult'], ParentType, ContextType, RequireFields<MutationSetBookmarkArticleArgs, 'input'>>;
setDeviceToken?: Resolver<ResolversTypes['SetDeviceTokenResult'], ParentType, ContextType, RequireFields<MutationSetDeviceTokenArgs, 'input'>>; setDeviceToken?: Resolver<ResolversTypes['SetDeviceTokenResult'], ParentType, ContextType, RequireFields<MutationSetDeviceTokenArgs, 'input'>>;
setFollow?: Resolver<ResolversTypes['SetFollowResult'], ParentType, ContextType, RequireFields<MutationSetFollowArgs, 'input'>>; setFollow?: Resolver<ResolversTypes['SetFollowResult'], ParentType, ContextType, RequireFields<MutationSetFollowArgs, 'input'>>;
setIntegration?: Resolver<ResolversTypes['SetIntegrationResult'], ParentType, ContextType, RequireFields<MutationSetIntegrationArgs, 'input'>>;
setLabels?: Resolver<ResolversTypes['SetLabelsResult'], ParentType, ContextType, RequireFields<MutationSetLabelsArgs, 'input'>>; setLabels?: Resolver<ResolversTypes['SetLabelsResult'], ParentType, ContextType, RequireFields<MutationSetLabelsArgs, 'input'>>;
setLabelsForHighlight?: Resolver<ResolversTypes['SetLabelsResult'], ParentType, ContextType, RequireFields<MutationSetLabelsForHighlightArgs, 'input'>>; setLabelsForHighlight?: Resolver<ResolversTypes['SetLabelsResult'], ParentType, ContextType, RequireFields<MutationSetLabelsForHighlightArgs, 'input'>>;
setLinkArchived?: Resolver<ResolversTypes['ArchiveLinkResult'], ParentType, ContextType, RequireFields<MutationSetLinkArchivedArgs, 'input'>>; setLinkArchived?: Resolver<ResolversTypes['ArchiveLinkResult'], ParentType, ContextType, RequireFields<MutationSetLinkArchivedArgs, 'input'>>;
@ -4167,6 +4158,20 @@ export type SetFollowSuccessResolvers<ContextType = ResolverContext, ParentType
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}; };
export type SetIntegrationErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['SetIntegrationError'] = ResolversParentTypes['SetIntegrationError']> = {
errorCodes?: Resolver<Array<ResolversTypes['SetIntegrationErrorCode']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type SetIntegrationResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['SetIntegrationResult'] = ResolversParentTypes['SetIntegrationResult']> = {
__resolveType: TypeResolveFn<'SetIntegrationError' | 'SetIntegrationSuccess', ParentType, ContextType>;
};
export type SetIntegrationSuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['SetIntegrationSuccess'] = ResolversParentTypes['SetIntegrationSuccess']> = {
integration?: Resolver<ResolversTypes['Integration'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type SetLabelsErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['SetLabelsError'] = ResolversParentTypes['SetLabelsError']> = { export type SetLabelsErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['SetLabelsError'] = ResolversParentTypes['SetLabelsError']> = {
errorCodes?: Resolver<Array<ResolversTypes['SetLabelsErrorCode']>, ParentType, ContextType>; errorCodes?: Resolver<Array<ResolversTypes['SetLabelsErrorCode']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
@ -4635,9 +4640,6 @@ export type Resolvers<ContextType = ResolverContext> = {
CreateHighlightReplySuccess?: CreateHighlightReplySuccessResolvers<ContextType>; CreateHighlightReplySuccess?: CreateHighlightReplySuccessResolvers<ContextType>;
CreateHighlightResult?: CreateHighlightResultResolvers<ContextType>; CreateHighlightResult?: CreateHighlightResultResolvers<ContextType>;
CreateHighlightSuccess?: CreateHighlightSuccessResolvers<ContextType>; CreateHighlightSuccess?: CreateHighlightSuccessResolvers<ContextType>;
CreateIntegrationError?: CreateIntegrationErrorResolvers<ContextType>;
CreateIntegrationResult?: CreateIntegrationResultResolvers<ContextType>;
CreateIntegrationSuccess?: CreateIntegrationSuccessResolvers<ContextType>;
CreateLabelError?: CreateLabelErrorResolvers<ContextType>; CreateLabelError?: CreateLabelErrorResolvers<ContextType>;
CreateLabelResult?: CreateLabelResultResolvers<ContextType>; CreateLabelResult?: CreateLabelResultResolvers<ContextType>;
CreateLabelSuccess?: CreateLabelSuccessResolvers<ContextType>; CreateLabelSuccess?: CreateLabelSuccessResolvers<ContextType>;
@ -4760,6 +4762,9 @@ export type Resolvers<ContextType = ResolverContext> = {
SetFollowError?: SetFollowErrorResolvers<ContextType>; SetFollowError?: SetFollowErrorResolvers<ContextType>;
SetFollowResult?: SetFollowResultResolvers<ContextType>; SetFollowResult?: SetFollowResultResolvers<ContextType>;
SetFollowSuccess?: SetFollowSuccessResolvers<ContextType>; SetFollowSuccess?: SetFollowSuccessResolvers<ContextType>;
SetIntegrationError?: SetIntegrationErrorResolvers<ContextType>;
SetIntegrationResult?: SetIntegrationResultResolvers<ContextType>;
SetIntegrationSuccess?: SetIntegrationSuccessResolvers<ContextType>;
SetLabelsError?: SetLabelsErrorResolvers<ContextType>; SetLabelsError?: SetLabelsErrorResolvers<ContextType>;
SetLabelsResult?: SetLabelsResultResolvers<ContextType>; SetLabelsResult?: SetLabelsResultResolvers<ContextType>;
SetLabelsSuccess?: SetLabelsSuccessResolvers<ContextType>; SetLabelsSuccess?: SetLabelsSuccessResolvers<ContextType>;

View file

@ -284,28 +284,6 @@ type CreateHighlightSuccess {
highlight: Highlight! highlight: Highlight!
} }
type CreateIntegrationError {
errorCodes: [CreateIntegrationErrorCode!]!
}
enum CreateIntegrationErrorCode {
BAD_REQUEST
INVALID_TOKEN
NOT_FOUND
UNAUTHORIZED
}
input CreateIntegrationInput {
token: String!
type: IntegrationType!
}
union CreateIntegrationResult = CreateIntegrationError | CreateIntegrationSuccess
type CreateIntegrationSuccess {
integration: Integration!
}
type CreateLabelError { type CreateLabelError {
errorCodes: [CreateLabelErrorCode!]! errorCodes: [CreateLabelErrorCode!]!
} }
@ -684,6 +662,7 @@ type Integration {
createdAt: Date! createdAt: Date!
enabled: Boolean! enabled: Boolean!
id: ID! id: ID!
token: String!
type: IntegrationType! type: IntegrationType!
updatedAt: Date! updatedAt: Date!
} }
@ -830,7 +809,6 @@ type Mutation {
createArticleSavingRequest(input: CreateArticleSavingRequestInput!): CreateArticleSavingRequestResult! createArticleSavingRequest(input: CreateArticleSavingRequestInput!): CreateArticleSavingRequestResult!
createHighlight(input: CreateHighlightInput!): CreateHighlightResult! createHighlight(input: CreateHighlightInput!): CreateHighlightResult!
createHighlightReply(input: CreateHighlightReplyInput!): CreateHighlightReplyResult! createHighlightReply(input: CreateHighlightReplyInput!): CreateHighlightReplyResult!
createIntegration(input: CreateIntegrationInput!): CreateIntegrationResult!
createLabel(input: CreateLabelInput!): CreateLabelResult! createLabel(input: CreateLabelInput!): CreateLabelResult!
createNewsletterEmail: CreateNewsletterEmailResult! createNewsletterEmail: CreateNewsletterEmailResult!
createReaction(input: CreateReactionInput!): CreateReactionResult! createReaction(input: CreateReactionInput!): CreateReactionResult!
@ -858,6 +836,7 @@ type Mutation {
setBookmarkArticle(input: SetBookmarkArticleInput!): SetBookmarkArticleResult! setBookmarkArticle(input: SetBookmarkArticleInput!): SetBookmarkArticleResult!
setDeviceToken(input: SetDeviceTokenInput!): SetDeviceTokenResult! setDeviceToken(input: SetDeviceTokenInput!): SetDeviceTokenResult!
setFollow(input: SetFollowInput!): SetFollowResult! setFollow(input: SetFollowInput!): SetFollowResult!
setIntegration(input: SetIntegrationInput!): SetIntegrationResult!
setLabels(input: SetLabelsInput!): SetLabelsResult! setLabels(input: SetLabelsInput!): SetLabelsResult!
setLabelsForHighlight(input: SetLabelsForHighlightInput!): SetLabelsResult! setLabelsForHighlight(input: SetLabelsForHighlightInput!): SetLabelsResult!
setLinkArchived(input: ArchiveLinkInput!): ArchiveLinkResult! setLinkArchived(input: ArchiveLinkInput!): ArchiveLinkResult!
@ -1260,6 +1239,31 @@ type SetFollowSuccess {
updatedUser: User! updatedUser: User!
} }
type SetIntegrationError {
errorCodes: [SetIntegrationErrorCode!]!
}
enum SetIntegrationErrorCode {
ALREADY_EXISTS
BAD_REQUEST
INVALID_TOKEN
NOT_FOUND
UNAUTHORIZED
}
input SetIntegrationInput {
enabled: Boolean
id: ID
token: String!
type: IntegrationType!
}
union SetIntegrationResult = SetIntegrationError | SetIntegrationSuccess
type SetIntegrationSuccess {
integration: Integration!
}
type SetLabelsError { type SetLabelsError {
errorCodes: [SetLabelsErrorCode!]! errorCodes: [SetLabelsErrorCode!]!
} }

View file

@ -97,6 +97,7 @@ import {
generateUploadFilePathName, generateUploadFilePathName,
} from '../utils/uploads' } from '../utils/uploads'
import { getPageByParam } from '../elastic/pages' import { getPageByParam } from '../elastic/pages'
import { setIntegrationResolver } from './integrations'
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
type ResultResolveType = { type ResultResolveType = {
@ -165,6 +166,7 @@ export const functionResolvers = {
revokeApiKey: revokeApiKeyResolver, revokeApiKey: revokeApiKeyResolver,
setLabelsForHighlight: setLabelsForHighlightResolver, setLabelsForHighlight: setLabelsForHighlightResolver,
moveLabel: moveLabelResolver, moveLabel: moveLabelResolver,
setIntegration: setIntegrationResolver,
}, },
Query: { Query: {
me: getMeUserResolver, me: getMeUserResolver,
@ -592,4 +594,5 @@ export const functionResolvers = {
...resultResolveTypeResolver('TypeaheadSearch'), ...resultResolveTypeResolver('TypeaheadSearch'),
...resultResolveTypeResolver('UpdatesSince'), ...resultResolveTypeResolver('UpdatesSince'),
...resultResolveTypeResolver('MoveLabel'), ...resultResolveTypeResolver('MoveLabel'),
...resultResolveTypeResolver('SetIntegration'),
} }

View file

@ -0,0 +1,97 @@
import { authorized } from '../../utils/helpers'
import {
MutationSetIntegrationArgs,
SetIntegrationError,
SetIntegrationErrorCode,
SetIntegrationSuccess,
} from '../../generated/graphql'
import { getRepository } from '../../entity/utils'
import { User } from '../../entity/user'
import { Integration } from '../../entity/integration'
import { analytics } from '../../utils/analytics'
import { env } from '../../env'
import { validateToken } from '../../services/integrations'
export const setIntegrationResolver = authorized<
SetIntegrationSuccess,
SetIntegrationError,
MutationSetIntegrationArgs
>(async (_, { input }, { claims: { uid }, log }) => {
log.info('setIntegrationResolver')
try {
const user = await getRepository(User).findOneBy({ id: uid })
if (!user) {
return {
errorCodes: [SetIntegrationErrorCode.Unauthorized],
}
}
const integrationToSave: Partial<Integration> = {
user,
token: input.token,
type: input.type,
enabled: input.enabled === null ? true : input.enabled,
}
if (input.id) {
// Update
const existingIntegration = await getRepository(Integration).findOne({
where: { id: input.id },
relations: ['user'],
})
if (!existingIntegration) {
return {
errorCodes: [SetIntegrationErrorCode.NotFound],
}
}
if (existingIntegration.user.id !== uid) {
return {
errorCodes: [SetIntegrationErrorCode.Unauthorized],
}
}
integrationToSave.id = input.id
} else {
// Create
const existingIntegration = await getRepository(Integration).findOneBy({
user: { id: uid },
type: input.type,
})
if (existingIntegration) {
return {
errorCodes: [SetIntegrationErrorCode.AlreadyExists],
}
}
// validate token
if (!(await validateToken(input.token, input.type))) {
return {
errorCodes: [SetIntegrationErrorCode.InvalidToken],
}
}
}
const integration = await getRepository(Integration).save(integrationToSave)
analytics.track({
userId: uid,
event: 'integration_set',
properties: {
id: integration.id,
env: env.server.apiEnv,
},
})
return {
integration,
}
} catch (error) {
log.error(error)
return {
errorCodes: [SetIntegrationErrorCode.BadRequest],
}
}
})

View file

@ -1812,17 +1812,16 @@ const schema = gql`
NOT_FOUND NOT_FOUND
} }
union CreateIntegrationResult = union SetIntegrationResult = SetIntegrationSuccess | SetIntegrationError
CreateIntegrationSuccess
| CreateIntegrationError
type CreateIntegrationSuccess { type SetIntegrationSuccess {
integration: Integration! integration: Integration!
} }
type Integration { type Integration {
id: ID! id: ID!
type: IntegrationType! type: IntegrationType!
token: String!
enabled: Boolean! enabled: Boolean!
createdAt: Date! createdAt: Date!
updatedAt: Date! updatedAt: Date!
@ -1832,20 +1831,23 @@ const schema = gql`
READWISE READWISE
} }
type CreateIntegrationError { type SetIntegrationError {
errorCodes: [CreateIntegrationErrorCode!]! errorCodes: [SetIntegrationErrorCode!]!
} }
enum CreateIntegrationErrorCode { enum SetIntegrationErrorCode {
UNAUTHORIZED UNAUTHORIZED
BAD_REQUEST BAD_REQUEST
NOT_FOUND NOT_FOUND
INVALID_TOKEN INVALID_TOKEN
ALREADY_EXISTS
} }
input CreateIntegrationInput { input SetIntegrationInput {
id: ID
type: IntegrationType! type: IntegrationType!
token: String! token: String!
enabled: Boolean
} }
# Mutations # Mutations
@ -1917,7 +1919,7 @@ const schema = gql`
revokeApiKey(id: ID!): RevokeApiKeyResult! revokeApiKey(id: ID!): RevokeApiKeyResult!
setLabelsForHighlight(input: SetLabelsForHighlightInput!): SetLabelsResult! setLabelsForHighlight(input: SetLabelsForHighlightInput!): SetLabelsResult!
moveLabel(input: MoveLabelInput!): MoveLabelResult! moveLabel(input: MoveLabelInput!): MoveLabelResult!
createIntegration(input: CreateIntegrationInput!): CreateIntegrationResult! setIntegration(input: SetIntegrationInput!): SetIntegrationResult!
} }
# FIXME: remove sort from feedArticles after all cached tabs are closed # FIXME: remove sort from feedArticles after all cached tabs are closed

View file

@ -0,0 +1,27 @@
import { IntegrationType } from '../generated/graphql'
import { env } from '../env'
import axios from 'axios'
const READWISE_API_URL = 'https://readwise.io/api/v2'
export const validateToken = async (
token: string,
type: IntegrationType
): Promise<boolean> => {
switch (type) {
case IntegrationType.Readwise:
return validateReadwiseToken(token)
default:
return false
}
}
const validateReadwiseToken = async (token: string): Promise<boolean> => {
const authUrl = `${env.readwise.apiUrl || READWISE_API_URL}/auth`
const response = await axios.get(authUrl, {
headers: {
Authorization: `Token ${token}`,
},
})
return response.status === 204
}

View file

@ -84,6 +84,9 @@ interface BackendEnv {
resetPasswordTemplateId: string resetPasswordTemplateId: string
installationTemplateId: string installationTemplateId: string
} }
readwise: {
apiUrl: string
}
} }
/*** /***
@ -132,6 +135,7 @@ const nullableEnvVars = [
'SENDGRID_REMINDER_TEMPLATE_ID', 'SENDGRID_REMINDER_TEMPLATE_ID',
'SENDGRID_RESET_PASSWORD_TEMPLATE_ID', 'SENDGRID_RESET_PASSWORD_TEMPLATE_ID',
'SENDGRID_INSTALLATION_TEMPLATE_ID', 'SENDGRID_INSTALLATION_TEMPLATE_ID',
'READWISE_API_URL',
] // Allow some vars to be null/empty ] // Allow some vars to be null/empty
/* If not in GAE and Prod/QA/Demo env (f.e. on localhost/dev env), allow following env vars to be null */ /* If not in GAE and Prod/QA/Demo env (f.e. on localhost/dev env), allow following env vars to be null */
@ -245,6 +249,10 @@ export function getEnv(): BackendEnv {
installationTemplateId: parse('SENDGRID_INSTALLATION_TEMPLATE_ID'), installationTemplateId: parse('SENDGRID_INSTALLATION_TEMPLATE_ID'),
} }
const readwise = {
apiUrl: parse('READWISE_API_URL'),
}
return { return {
pg, pg,
client, client,
@ -262,6 +270,7 @@ export function getEnv(): BackendEnv {
elastic, elastic,
sender, sender,
sendgrid, sendgrid,
readwise,
} }
} }