Add createIntegration API implementation
This commit is contained in:
@ -25,7 +25,7 @@ export class Integration {
|
||||
@Column('enum', { enum: IntegrationType })
|
||||
type!: IntegrationType
|
||||
|
||||
@Column('varchar')
|
||||
@Column('varchar', { length: 255 })
|
||||
token!: string
|
||||
|
||||
@Column('boolean', { default: true })
|
||||
|
||||
@ -331,30 +331,6 @@ export type CreateHighlightSuccess = {
|
||||
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 = {
|
||||
__typename?: 'CreateLabelError';
|
||||
errorCodes: Array<CreateLabelErrorCode>;
|
||||
@ -774,6 +750,7 @@ export type Integration = {
|
||||
createdAt: Scalars['Date'];
|
||||
enabled: Scalars['Boolean'];
|
||||
id: Scalars['ID'];
|
||||
token: Scalars['String'];
|
||||
type: IntegrationType;
|
||||
updatedAt: Scalars['Date'];
|
||||
};
|
||||
@ -934,7 +911,6 @@ export type Mutation = {
|
||||
createArticleSavingRequest: CreateArticleSavingRequestResult;
|
||||
createHighlight: CreateHighlightResult;
|
||||
createHighlightReply: CreateHighlightReplyResult;
|
||||
createIntegration: CreateIntegrationResult;
|
||||
createLabel: CreateLabelResult;
|
||||
createNewsletterEmail: CreateNewsletterEmailResult;
|
||||
createReaction: CreateReactionResult;
|
||||
@ -962,6 +938,7 @@ export type Mutation = {
|
||||
setBookmarkArticle: SetBookmarkArticleResult;
|
||||
setDeviceToken: SetDeviceTokenResult;
|
||||
setFollow: SetFollowResult;
|
||||
setIntegration: SetIntegrationResult;
|
||||
setLabels: SetLabelsResult;
|
||||
setLabelsForHighlight: SetLabelsResult;
|
||||
setLinkArchived: ArchiveLinkResult;
|
||||
@ -1009,11 +986,6 @@ export type MutationCreateHighlightReplyArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateIntegrationArgs = {
|
||||
input: CreateIntegrationInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateLabelArgs = {
|
||||
input: CreateLabelInput;
|
||||
};
|
||||
@ -1139,6 +1111,11 @@ export type MutationSetFollowArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationSetIntegrationArgs = {
|
||||
input: SetIntegrationInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationSetLabelsArgs = {
|
||||
input: SetLabelsInput;
|
||||
};
|
||||
@ -1737,6 +1714,33 @@ export type SetFollowSuccess = {
|
||||
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 = {
|
||||
__typename?: 'SetLabelsError';
|
||||
errorCodes: Array<SetLabelsErrorCode>;
|
||||
@ -2548,11 +2552,6 @@ export type ResolversTypes = {
|
||||
CreateHighlightReplySuccess: ResolverTypeWrapper<CreateHighlightReplySuccess>;
|
||||
CreateHighlightResult: ResolversTypes['CreateHighlightError'] | ResolversTypes['CreateHighlightSuccess'];
|
||||
CreateHighlightSuccess: ResolverTypeWrapper<CreateHighlightSuccess>;
|
||||
CreateIntegrationError: ResolverTypeWrapper<CreateIntegrationError>;
|
||||
CreateIntegrationErrorCode: CreateIntegrationErrorCode;
|
||||
CreateIntegrationInput: CreateIntegrationInput;
|
||||
CreateIntegrationResult: ResolversTypes['CreateIntegrationError'] | ResolversTypes['CreateIntegrationSuccess'];
|
||||
CreateIntegrationSuccess: ResolverTypeWrapper<CreateIntegrationSuccess>;
|
||||
CreateLabelError: ResolverTypeWrapper<CreateLabelError>;
|
||||
CreateLabelErrorCode: CreateLabelErrorCode;
|
||||
CreateLabelInput: CreateLabelInput;
|
||||
@ -2732,6 +2731,11 @@ export type ResolversTypes = {
|
||||
SetFollowInput: SetFollowInput;
|
||||
SetFollowResult: ResolversTypes['SetFollowError'] | ResolversTypes['SetFollowSuccess'];
|
||||
SetFollowSuccess: ResolverTypeWrapper<SetFollowSuccess>;
|
||||
SetIntegrationError: ResolverTypeWrapper<SetIntegrationError>;
|
||||
SetIntegrationErrorCode: SetIntegrationErrorCode;
|
||||
SetIntegrationInput: SetIntegrationInput;
|
||||
SetIntegrationResult: ResolversTypes['SetIntegrationError'] | ResolversTypes['SetIntegrationSuccess'];
|
||||
SetIntegrationSuccess: ResolverTypeWrapper<SetIntegrationSuccess>;
|
||||
SetLabelsError: ResolverTypeWrapper<SetLabelsError>;
|
||||
SetLabelsErrorCode: SetLabelsErrorCode;
|
||||
SetLabelsForHighlightInput: SetLabelsForHighlightInput;
|
||||
@ -2909,10 +2913,6 @@ export type ResolversParentTypes = {
|
||||
CreateHighlightReplySuccess: CreateHighlightReplySuccess;
|
||||
CreateHighlightResult: ResolversParentTypes['CreateHighlightError'] | ResolversParentTypes['CreateHighlightSuccess'];
|
||||
CreateHighlightSuccess: CreateHighlightSuccess;
|
||||
CreateIntegrationError: CreateIntegrationError;
|
||||
CreateIntegrationInput: CreateIntegrationInput;
|
||||
CreateIntegrationResult: ResolversParentTypes['CreateIntegrationError'] | ResolversParentTypes['CreateIntegrationSuccess'];
|
||||
CreateIntegrationSuccess: CreateIntegrationSuccess;
|
||||
CreateLabelError: CreateLabelError;
|
||||
CreateLabelInput: CreateLabelInput;
|
||||
CreateLabelResult: ResolversParentTypes['CreateLabelError'] | ResolversParentTypes['CreateLabelSuccess'];
|
||||
@ -3056,6 +3056,10 @@ export type ResolversParentTypes = {
|
||||
SetFollowInput: SetFollowInput;
|
||||
SetFollowResult: ResolversParentTypes['SetFollowError'] | ResolversParentTypes['SetFollowSuccess'];
|
||||
SetFollowSuccess: SetFollowSuccess;
|
||||
SetIntegrationError: SetIntegrationError;
|
||||
SetIntegrationInput: SetIntegrationInput;
|
||||
SetIntegrationResult: ResolversParentTypes['SetIntegrationError'] | ResolversParentTypes['SetIntegrationSuccess'];
|
||||
SetIntegrationSuccess: SetIntegrationSuccess;
|
||||
SetLabelsError: SetLabelsError;
|
||||
SetLabelsForHighlightInput: SetLabelsForHighlightInput;
|
||||
SetLabelsInput: SetLabelsInput;
|
||||
@ -3382,20 +3386,6 @@ export type CreateHighlightSuccessResolvers<ContextType = ResolverContext, Paren
|
||||
__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']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['CreateLabelErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
@ -3717,6 +3707,7 @@ export type IntegrationResolvers<ContextType = ResolverContext, ParentType exten
|
||||
createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
enabled?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
token?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
type?: Resolver<ResolversTypes['IntegrationType'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], 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'>>;
|
||||
createHighlight?: Resolver<ResolversTypes['CreateHighlightResult'], ParentType, ContextType, RequireFields<MutationCreateHighlightArgs, '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'>>;
|
||||
createNewsletterEmail?: Resolver<ResolversTypes['CreateNewsletterEmailResult'], ParentType, ContextType>;
|
||||
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'>>;
|
||||
setDeviceToken?: Resolver<ResolversTypes['SetDeviceTokenResult'], ParentType, ContextType, RequireFields<MutationSetDeviceTokenArgs, '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'>>;
|
||||
setLabelsForHighlight?: Resolver<ResolversTypes['SetLabelsResult'], ParentType, ContextType, RequireFields<MutationSetLabelsForHighlightArgs, 'input'>>;
|
||||
setLinkArchived?: Resolver<ResolversTypes['ArchiveLinkResult'], ParentType, ContextType, RequireFields<MutationSetLinkArchivedArgs, 'input'>>;
|
||||
@ -4167,6 +4158,20 @@ export type SetFollowSuccessResolvers<ContextType = ResolverContext, ParentType
|
||||
__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']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['SetLabelsErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
@ -4635,9 +4640,6 @@ export type Resolvers<ContextType = ResolverContext> = {
|
||||
CreateHighlightReplySuccess?: CreateHighlightReplySuccessResolvers<ContextType>;
|
||||
CreateHighlightResult?: CreateHighlightResultResolvers<ContextType>;
|
||||
CreateHighlightSuccess?: CreateHighlightSuccessResolvers<ContextType>;
|
||||
CreateIntegrationError?: CreateIntegrationErrorResolvers<ContextType>;
|
||||
CreateIntegrationResult?: CreateIntegrationResultResolvers<ContextType>;
|
||||
CreateIntegrationSuccess?: CreateIntegrationSuccessResolvers<ContextType>;
|
||||
CreateLabelError?: CreateLabelErrorResolvers<ContextType>;
|
||||
CreateLabelResult?: CreateLabelResultResolvers<ContextType>;
|
||||
CreateLabelSuccess?: CreateLabelSuccessResolvers<ContextType>;
|
||||
@ -4760,6 +4762,9 @@ export type Resolvers<ContextType = ResolverContext> = {
|
||||
SetFollowError?: SetFollowErrorResolvers<ContextType>;
|
||||
SetFollowResult?: SetFollowResultResolvers<ContextType>;
|
||||
SetFollowSuccess?: SetFollowSuccessResolvers<ContextType>;
|
||||
SetIntegrationError?: SetIntegrationErrorResolvers<ContextType>;
|
||||
SetIntegrationResult?: SetIntegrationResultResolvers<ContextType>;
|
||||
SetIntegrationSuccess?: SetIntegrationSuccessResolvers<ContextType>;
|
||||
SetLabelsError?: SetLabelsErrorResolvers<ContextType>;
|
||||
SetLabelsResult?: SetLabelsResultResolvers<ContextType>;
|
||||
SetLabelsSuccess?: SetLabelsSuccessResolvers<ContextType>;
|
||||
|
||||
@ -284,28 +284,6 @@ type CreateHighlightSuccess {
|
||||
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 {
|
||||
errorCodes: [CreateLabelErrorCode!]!
|
||||
}
|
||||
@ -684,6 +662,7 @@ type Integration {
|
||||
createdAt: Date!
|
||||
enabled: Boolean!
|
||||
id: ID!
|
||||
token: String!
|
||||
type: IntegrationType!
|
||||
updatedAt: Date!
|
||||
}
|
||||
@ -830,7 +809,6 @@ type Mutation {
|
||||
createArticleSavingRequest(input: CreateArticleSavingRequestInput!): CreateArticleSavingRequestResult!
|
||||
createHighlight(input: CreateHighlightInput!): CreateHighlightResult!
|
||||
createHighlightReply(input: CreateHighlightReplyInput!): CreateHighlightReplyResult!
|
||||
createIntegration(input: CreateIntegrationInput!): CreateIntegrationResult!
|
||||
createLabel(input: CreateLabelInput!): CreateLabelResult!
|
||||
createNewsletterEmail: CreateNewsletterEmailResult!
|
||||
createReaction(input: CreateReactionInput!): CreateReactionResult!
|
||||
@ -858,6 +836,7 @@ type Mutation {
|
||||
setBookmarkArticle(input: SetBookmarkArticleInput!): SetBookmarkArticleResult!
|
||||
setDeviceToken(input: SetDeviceTokenInput!): SetDeviceTokenResult!
|
||||
setFollow(input: SetFollowInput!): SetFollowResult!
|
||||
setIntegration(input: SetIntegrationInput!): SetIntegrationResult!
|
||||
setLabels(input: SetLabelsInput!): SetLabelsResult!
|
||||
setLabelsForHighlight(input: SetLabelsForHighlightInput!): SetLabelsResult!
|
||||
setLinkArchived(input: ArchiveLinkInput!): ArchiveLinkResult!
|
||||
@ -1260,6 +1239,31 @@ type SetFollowSuccess {
|
||||
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 {
|
||||
errorCodes: [SetLabelsErrorCode!]!
|
||||
}
|
||||
|
||||
@ -97,6 +97,7 @@ import {
|
||||
generateUploadFilePathName,
|
||||
} from '../utils/uploads'
|
||||
import { getPageByParam } from '../elastic/pages'
|
||||
import { setIntegrationResolver } from './integrations'
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
type ResultResolveType = {
|
||||
@ -165,6 +166,7 @@ export const functionResolvers = {
|
||||
revokeApiKey: revokeApiKeyResolver,
|
||||
setLabelsForHighlight: setLabelsForHighlightResolver,
|
||||
moveLabel: moveLabelResolver,
|
||||
setIntegration: setIntegrationResolver,
|
||||
},
|
||||
Query: {
|
||||
me: getMeUserResolver,
|
||||
@ -592,4 +594,5 @@ export const functionResolvers = {
|
||||
...resultResolveTypeResolver('TypeaheadSearch'),
|
||||
...resultResolveTypeResolver('UpdatesSince'),
|
||||
...resultResolveTypeResolver('MoveLabel'),
|
||||
...resultResolveTypeResolver('SetIntegration'),
|
||||
}
|
||||
|
||||
97
packages/api/src/resolvers/integrations/index.ts
Normal file
97
packages/api/src/resolvers/integrations/index.ts
Normal 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],
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1812,17 +1812,16 @@ const schema = gql`
|
||||
NOT_FOUND
|
||||
}
|
||||
|
||||
union CreateIntegrationResult =
|
||||
CreateIntegrationSuccess
|
||||
| CreateIntegrationError
|
||||
union SetIntegrationResult = SetIntegrationSuccess | SetIntegrationError
|
||||
|
||||
type CreateIntegrationSuccess {
|
||||
type SetIntegrationSuccess {
|
||||
integration: Integration!
|
||||
}
|
||||
|
||||
type Integration {
|
||||
id: ID!
|
||||
type: IntegrationType!
|
||||
token: String!
|
||||
enabled: Boolean!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
@ -1832,20 +1831,23 @@ const schema = gql`
|
||||
READWISE
|
||||
}
|
||||
|
||||
type CreateIntegrationError {
|
||||
errorCodes: [CreateIntegrationErrorCode!]!
|
||||
type SetIntegrationError {
|
||||
errorCodes: [SetIntegrationErrorCode!]!
|
||||
}
|
||||
|
||||
enum CreateIntegrationErrorCode {
|
||||
enum SetIntegrationErrorCode {
|
||||
UNAUTHORIZED
|
||||
BAD_REQUEST
|
||||
NOT_FOUND
|
||||
INVALID_TOKEN
|
||||
ALREADY_EXISTS
|
||||
}
|
||||
|
||||
input CreateIntegrationInput {
|
||||
input SetIntegrationInput {
|
||||
id: ID
|
||||
type: IntegrationType!
|
||||
token: String!
|
||||
enabled: Boolean
|
||||
}
|
||||
|
||||
# Mutations
|
||||
@ -1917,7 +1919,7 @@ const schema = gql`
|
||||
revokeApiKey(id: ID!): RevokeApiKeyResult!
|
||||
setLabelsForHighlight(input: SetLabelsForHighlightInput!): SetLabelsResult!
|
||||
moveLabel(input: MoveLabelInput!): MoveLabelResult!
|
||||
createIntegration(input: CreateIntegrationInput!): CreateIntegrationResult!
|
||||
setIntegration(input: SetIntegrationInput!): SetIntegrationResult!
|
||||
}
|
||||
|
||||
# FIXME: remove sort from feedArticles after all cached tabs are closed
|
||||
|
||||
27
packages/api/src/services/integrations.ts
Normal file
27
packages/api/src/services/integrations.ts
Normal 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
|
||||
}
|
||||
@ -84,6 +84,9 @@ interface BackendEnv {
|
||||
resetPasswordTemplateId: string
|
||||
installationTemplateId: string
|
||||
}
|
||||
readwise: {
|
||||
apiUrl: string
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
@ -132,6 +135,7 @@ const nullableEnvVars = [
|
||||
'SENDGRID_REMINDER_TEMPLATE_ID',
|
||||
'SENDGRID_RESET_PASSWORD_TEMPLATE_ID',
|
||||
'SENDGRID_INSTALLATION_TEMPLATE_ID',
|
||||
'READWISE_API_URL',
|
||||
] // 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 */
|
||||
@ -245,6 +249,10 @@ export function getEnv(): BackendEnv {
|
||||
installationTemplateId: parse('SENDGRID_INSTALLATION_TEMPLATE_ID'),
|
||||
}
|
||||
|
||||
const readwise = {
|
||||
apiUrl: parse('READWISE_API_URL'),
|
||||
}
|
||||
|
||||
return {
|
||||
pg,
|
||||
client,
|
||||
@ -262,6 +270,7 @@ export function getEnv(): BackendEnv {
|
||||
elastic,
|
||||
sender,
|
||||
sendgrid,
|
||||
readwise,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user