mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Add test for revoke api key
This commit is contained in:
parent
b77b378221
commit
b7206457b4
6 changed files with 193 additions and 118 deletions
|
|
@ -39,6 +39,7 @@ export type ApiKey = {
|
|||
createdAt: Scalars['Date'];
|
||||
expiresAt: Scalars['Date'];
|
||||
id: Scalars['ID'];
|
||||
key?: Maybe<Scalars['String']>;
|
||||
name: Scalars['String'];
|
||||
scopes?: Maybe<Array<Scalars['String']>>;
|
||||
usedAt?: Maybe<Scalars['Date']>;
|
||||
|
|
@ -421,24 +422,6 @@ export type CreateReminderSuccess = {
|
|||
reminder: Reminder;
|
||||
};
|
||||
|
||||
export type DeleteApiKeyError = {
|
||||
__typename?: 'DeleteApiKeyError';
|
||||
errorCodes: Array<DeleteApiKeyErrorCode>;
|
||||
};
|
||||
|
||||
export enum DeleteApiKeyErrorCode {
|
||||
BadRequest = 'BAD_REQUEST',
|
||||
NotFound = 'NOT_FOUND',
|
||||
Unauthorized = 'UNAUTHORIZED'
|
||||
}
|
||||
|
||||
export type DeleteApiKeyResult = DeleteApiKeyError | DeleteApiKeySuccess;
|
||||
|
||||
export type DeleteApiKeySuccess = {
|
||||
__typename?: 'DeleteApiKeySuccess';
|
||||
apiKey: ApiKey;
|
||||
};
|
||||
|
||||
export type DeleteHighlightError = {
|
||||
__typename?: 'DeleteHighlightError';
|
||||
errorCodes: Array<DeleteHighlightErrorCode>;
|
||||
|
|
@ -630,7 +613,7 @@ export type GenerateApiKeyResult = GenerateApiKeyError | GenerateApiKeySuccess;
|
|||
|
||||
export type GenerateApiKeySuccess = {
|
||||
__typename?: 'GenerateApiKeySuccess';
|
||||
apiKey: Scalars['String'];
|
||||
apiKey: ApiKey;
|
||||
};
|
||||
|
||||
export type GetFollowersError = {
|
||||
|
|
@ -877,7 +860,6 @@ export type Mutation = {
|
|||
createNewsletterEmail: CreateNewsletterEmailResult;
|
||||
createReaction: CreateReactionResult;
|
||||
createReminder: CreateReminderResult;
|
||||
deleteApiKey: DeleteApiKeyResult;
|
||||
deleteHighlight: DeleteHighlightResult;
|
||||
deleteHighlightReply: DeleteHighlightReplyResult;
|
||||
deleteLabel: DeleteLabelResult;
|
||||
|
|
@ -892,6 +874,7 @@ export type Mutation = {
|
|||
logOut: LogOutResult;
|
||||
mergeHighlight: MergeHighlightResult;
|
||||
reportItem: ReportItemResult;
|
||||
revokeApiKey: RevokeApiKeyResult;
|
||||
saveArticleReadingProgress: SaveArticleReadingProgressResult;
|
||||
saveFile: SaveResult;
|
||||
savePage: SaveResult;
|
||||
|
|
@ -961,11 +944,6 @@ export type MutationCreateReminderArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type MutationDeleteApiKeyArgs = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteHighlightArgs = {
|
||||
highlightId: Scalars['ID'];
|
||||
};
|
||||
|
|
@ -1031,6 +1009,11 @@ export type MutationReportItemArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type MutationRevokeApiKeyArgs = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationSaveArticleReadingProgressArgs = {
|
||||
input: SaveArticleReadingProgressInput;
|
||||
};
|
||||
|
|
@ -1420,6 +1403,24 @@ export enum ReportType {
|
|||
Spam = 'SPAM'
|
||||
}
|
||||
|
||||
export type RevokeApiKeyError = {
|
||||
__typename?: 'RevokeApiKeyError';
|
||||
errorCodes: Array<RevokeApiKeyErrorCode>;
|
||||
};
|
||||
|
||||
export enum RevokeApiKeyErrorCode {
|
||||
BadRequest = 'BAD_REQUEST',
|
||||
NotFound = 'NOT_FOUND',
|
||||
Unauthorized = 'UNAUTHORIZED'
|
||||
}
|
||||
|
||||
export type RevokeApiKeyResult = RevokeApiKeyError | RevokeApiKeySuccess;
|
||||
|
||||
export type RevokeApiKeySuccess = {
|
||||
__typename?: 'RevokeApiKeySuccess';
|
||||
apiKey: ApiKey;
|
||||
};
|
||||
|
||||
export type SaveArticleReadingProgressError = {
|
||||
__typename?: 'SaveArticleReadingProgressError';
|
||||
errorCodes: Array<SaveArticleReadingProgressErrorCode>;
|
||||
|
|
@ -2394,10 +2395,6 @@ export type ResolversTypes = {
|
|||
CreateReminderResult: ResolversTypes['CreateReminderError'] | ResolversTypes['CreateReminderSuccess'];
|
||||
CreateReminderSuccess: ResolverTypeWrapper<CreateReminderSuccess>;
|
||||
Date: ResolverTypeWrapper<Scalars['Date']>;
|
||||
DeleteApiKeyError: ResolverTypeWrapper<DeleteApiKeyError>;
|
||||
DeleteApiKeyErrorCode: DeleteApiKeyErrorCode;
|
||||
DeleteApiKeyResult: ResolversTypes['DeleteApiKeyError'] | ResolversTypes['DeleteApiKeySuccess'];
|
||||
DeleteApiKeySuccess: ResolverTypeWrapper<DeleteApiKeySuccess>;
|
||||
DeleteHighlightError: ResolverTypeWrapper<DeleteHighlightError>;
|
||||
DeleteHighlightErrorCode: DeleteHighlightErrorCode;
|
||||
DeleteHighlightReplyError: ResolverTypeWrapper<DeleteHighlightReplyError>;
|
||||
|
|
@ -2506,6 +2503,10 @@ export type ResolversTypes = {
|
|||
ReportItemInput: ReportItemInput;
|
||||
ReportItemResult: ResolverTypeWrapper<ReportItemResult>;
|
||||
ReportType: ReportType;
|
||||
RevokeApiKeyError: ResolverTypeWrapper<RevokeApiKeyError>;
|
||||
RevokeApiKeyErrorCode: RevokeApiKeyErrorCode;
|
||||
RevokeApiKeyResult: ResolversTypes['RevokeApiKeyError'] | ResolversTypes['RevokeApiKeySuccess'];
|
||||
RevokeApiKeySuccess: ResolverTypeWrapper<RevokeApiKeySuccess>;
|
||||
SaveArticleReadingProgressError: ResolverTypeWrapper<SaveArticleReadingProgressError>;
|
||||
SaveArticleReadingProgressErrorCode: SaveArticleReadingProgressErrorCode;
|
||||
SaveArticleReadingProgressInput: SaveArticleReadingProgressInput;
|
||||
|
|
@ -2724,9 +2725,6 @@ export type ResolversParentTypes = {
|
|||
CreateReminderResult: ResolversParentTypes['CreateReminderError'] | ResolversParentTypes['CreateReminderSuccess'];
|
||||
CreateReminderSuccess: CreateReminderSuccess;
|
||||
Date: Scalars['Date'];
|
||||
DeleteApiKeyError: DeleteApiKeyError;
|
||||
DeleteApiKeyResult: ResolversParentTypes['DeleteApiKeyError'] | ResolversParentTypes['DeleteApiKeySuccess'];
|
||||
DeleteApiKeySuccess: DeleteApiKeySuccess;
|
||||
DeleteHighlightError: DeleteHighlightError;
|
||||
DeleteHighlightReplyError: DeleteHighlightReplyError;
|
||||
DeleteHighlightReplyResult: ResolversParentTypes['DeleteHighlightReplyError'] | ResolversParentTypes['DeleteHighlightReplySuccess'];
|
||||
|
|
@ -2814,6 +2812,9 @@ export type ResolversParentTypes = {
|
|||
ReminderSuccess: ReminderSuccess;
|
||||
ReportItemInput: ReportItemInput;
|
||||
ReportItemResult: ReportItemResult;
|
||||
RevokeApiKeyError: RevokeApiKeyError;
|
||||
RevokeApiKeyResult: ResolversParentTypes['RevokeApiKeyError'] | ResolversParentTypes['RevokeApiKeySuccess'];
|
||||
RevokeApiKeySuccess: RevokeApiKeySuccess;
|
||||
SaveArticleReadingProgressError: SaveArticleReadingProgressError;
|
||||
SaveArticleReadingProgressInput: SaveArticleReadingProgressInput;
|
||||
SaveArticleReadingProgressResult: ResolversParentTypes['SaveArticleReadingProgressError'] | ResolversParentTypes['SaveArticleReadingProgressSuccess'];
|
||||
|
|
@ -2964,6 +2965,7 @@ export type ApiKeyResolvers<ContextType = ResolverContext, ParentType extends Re
|
|||
createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
expiresAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
key?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
scopes?: Resolver<Maybe<Array<ResolversTypes['String']>>, ParentType, ContextType>;
|
||||
usedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
|
|
@ -3219,20 +3221,6 @@ export interface DateScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes
|
|||
name: 'Date';
|
||||
}
|
||||
|
||||
export type DeleteApiKeyErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['DeleteApiKeyError'] = ResolversParentTypes['DeleteApiKeyError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['DeleteApiKeyErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type DeleteApiKeyResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['DeleteApiKeyResult'] = ResolversParentTypes['DeleteApiKeyResult']> = {
|
||||
__resolveType: TypeResolveFn<'DeleteApiKeyError' | 'DeleteApiKeySuccess', ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type DeleteApiKeySuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['DeleteApiKeySuccess'] = ResolversParentTypes['DeleteApiKeySuccess']> = {
|
||||
apiKey?: Resolver<ResolversTypes['ApiKey'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type DeleteHighlightErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['DeleteHighlightError'] = ResolversParentTypes['DeleteHighlightError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['DeleteHighlightErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
|
|
@ -3383,7 +3371,7 @@ export type GenerateApiKeyResultResolvers<ContextType = ResolverContext, ParentT
|
|||
};
|
||||
|
||||
export type GenerateApiKeySuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['GenerateApiKeySuccess'] = ResolversParentTypes['GenerateApiKeySuccess']> = {
|
||||
apiKey?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
apiKey?: Resolver<ResolversTypes['ApiKey'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
|
|
@ -3575,7 +3563,6 @@ export type MutationResolvers<ContextType = ResolverContext, ParentType extends
|
|||
createNewsletterEmail?: Resolver<ResolversTypes['CreateNewsletterEmailResult'], ParentType, ContextType>;
|
||||
createReaction?: Resolver<ResolversTypes['CreateReactionResult'], ParentType, ContextType, RequireFields<MutationCreateReactionArgs, 'input'>>;
|
||||
createReminder?: Resolver<ResolversTypes['CreateReminderResult'], ParentType, ContextType, RequireFields<MutationCreateReminderArgs, 'input'>>;
|
||||
deleteApiKey?: Resolver<ResolversTypes['DeleteApiKeyResult'], ParentType, ContextType, RequireFields<MutationDeleteApiKeyArgs, 'id'>>;
|
||||
deleteHighlight?: Resolver<ResolversTypes['DeleteHighlightResult'], ParentType, ContextType, RequireFields<MutationDeleteHighlightArgs, 'highlightId'>>;
|
||||
deleteHighlightReply?: Resolver<ResolversTypes['DeleteHighlightReplyResult'], ParentType, ContextType, RequireFields<MutationDeleteHighlightReplyArgs, 'highlightReplyId'>>;
|
||||
deleteLabel?: Resolver<ResolversTypes['DeleteLabelResult'], ParentType, ContextType, RequireFields<MutationDeleteLabelArgs, 'id'>>;
|
||||
|
|
@ -3590,6 +3577,7 @@ export type MutationResolvers<ContextType = ResolverContext, ParentType extends
|
|||
logOut?: Resolver<ResolversTypes['LogOutResult'], ParentType, ContextType>;
|
||||
mergeHighlight?: Resolver<ResolversTypes['MergeHighlightResult'], ParentType, ContextType, RequireFields<MutationMergeHighlightArgs, 'input'>>;
|
||||
reportItem?: Resolver<ResolversTypes['ReportItemResult'], ParentType, ContextType, RequireFields<MutationReportItemArgs, 'input'>>;
|
||||
revokeApiKey?: Resolver<ResolversTypes['RevokeApiKeyResult'], ParentType, ContextType, RequireFields<MutationRevokeApiKeyArgs, 'id'>>;
|
||||
saveArticleReadingProgress?: Resolver<ResolversTypes['SaveArticleReadingProgressResult'], ParentType, ContextType, RequireFields<MutationSaveArticleReadingProgressArgs, 'input'>>;
|
||||
saveFile?: Resolver<ResolversTypes['SaveResult'], ParentType, ContextType, RequireFields<MutationSaveFileArgs, 'input'>>;
|
||||
savePage?: Resolver<ResolversTypes['SaveResult'], ParentType, ContextType, RequireFields<MutationSavePageArgs, 'input'>>;
|
||||
|
|
@ -3742,6 +3730,20 @@ export type ReportItemResultResolvers<ContextType = ResolverContext, ParentType
|
|||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type RevokeApiKeyErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['RevokeApiKeyError'] = ResolversParentTypes['RevokeApiKeyError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['RevokeApiKeyErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type RevokeApiKeyResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['RevokeApiKeyResult'] = ResolversParentTypes['RevokeApiKeyResult']> = {
|
||||
__resolveType: TypeResolveFn<'RevokeApiKeyError' | 'RevokeApiKeySuccess', ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type RevokeApiKeySuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['RevokeApiKeySuccess'] = ResolversParentTypes['RevokeApiKeySuccess']> = {
|
||||
apiKey?: Resolver<ResolversTypes['ApiKey'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type SaveArticleReadingProgressErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['SaveArticleReadingProgressError'] = ResolversParentTypes['SaveArticleReadingProgressError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['SaveArticleReadingProgressErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
|
|
@ -4316,9 +4318,6 @@ export type Resolvers<ContextType = ResolverContext> = {
|
|||
CreateReminderResult?: CreateReminderResultResolvers<ContextType>;
|
||||
CreateReminderSuccess?: CreateReminderSuccessResolvers<ContextType>;
|
||||
Date?: GraphQLScalarType;
|
||||
DeleteApiKeyError?: DeleteApiKeyErrorResolvers<ContextType>;
|
||||
DeleteApiKeyResult?: DeleteApiKeyResultResolvers<ContextType>;
|
||||
DeleteApiKeySuccess?: DeleteApiKeySuccessResolvers<ContextType>;
|
||||
DeleteHighlightError?: DeleteHighlightErrorResolvers<ContextType>;
|
||||
DeleteHighlightReplyError?: DeleteHighlightReplyErrorResolvers<ContextType>;
|
||||
DeleteHighlightReplyResult?: DeleteHighlightReplyResultResolvers<ContextType>;
|
||||
|
|
@ -4395,6 +4394,9 @@ export type Resolvers<ContextType = ResolverContext> = {
|
|||
ReminderResult?: ReminderResultResolvers<ContextType>;
|
||||
ReminderSuccess?: ReminderSuccessResolvers<ContextType>;
|
||||
ReportItemResult?: ReportItemResultResolvers<ContextType>;
|
||||
RevokeApiKeyError?: RevokeApiKeyErrorResolvers<ContextType>;
|
||||
RevokeApiKeyResult?: RevokeApiKeyResultResolvers<ContextType>;
|
||||
RevokeApiKeySuccess?: RevokeApiKeySuccessResolvers<ContextType>;
|
||||
SaveArticleReadingProgressError?: SaveArticleReadingProgressErrorResolvers<ContextType>;
|
||||
SaveArticleReadingProgressResult?: SaveArticleReadingProgressResultResolvers<ContextType>;
|
||||
SaveArticleReadingProgressSuccess?: SaveArticleReadingProgressSuccessResolvers<ContextType>;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ type ApiKey {
|
|||
createdAt: Date!
|
||||
expiresAt: Date!
|
||||
id: ID!
|
||||
key: String
|
||||
name: String!
|
||||
scopes: [String!]
|
||||
usedAt: Date
|
||||
|
|
@ -368,22 +369,6 @@ type CreateReminderSuccess {
|
|||
|
||||
scalar Date
|
||||
|
||||
type DeleteApiKeyError {
|
||||
errorCodes: [DeleteApiKeyErrorCode!]!
|
||||
}
|
||||
|
||||
enum DeleteApiKeyErrorCode {
|
||||
BAD_REQUEST
|
||||
NOT_FOUND
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union DeleteApiKeyResult = DeleteApiKeyError | DeleteApiKeySuccess
|
||||
|
||||
type DeleteApiKeySuccess {
|
||||
apiKey: ApiKey!
|
||||
}
|
||||
|
||||
type DeleteHighlightError {
|
||||
errorCodes: [DeleteHighlightErrorCode!]!
|
||||
}
|
||||
|
|
@ -554,7 +539,7 @@ input GenerateApiKeyInput {
|
|||
union GenerateApiKeyResult = GenerateApiKeyError | GenerateApiKeySuccess
|
||||
|
||||
type GenerateApiKeySuccess {
|
||||
apiKey: String!
|
||||
apiKey: ApiKey!
|
||||
}
|
||||
|
||||
type GetFollowersError {
|
||||
|
|
@ -778,7 +763,6 @@ type Mutation {
|
|||
createNewsletterEmail: CreateNewsletterEmailResult!
|
||||
createReaction(input: CreateReactionInput!): CreateReactionResult!
|
||||
createReminder(input: CreateReminderInput!): CreateReminderResult!
|
||||
deleteApiKey(id: ID!): DeleteApiKeyResult!
|
||||
deleteHighlight(highlightId: ID!): DeleteHighlightResult!
|
||||
deleteHighlightReply(highlightReplyId: ID!): DeleteHighlightReplyResult!
|
||||
deleteLabel(id: ID!): DeleteLabelResult!
|
||||
|
|
@ -793,6 +777,7 @@ type Mutation {
|
|||
logOut: LogOutResult!
|
||||
mergeHighlight(input: MergeHighlightInput!): MergeHighlightResult!
|
||||
reportItem(input: ReportItemInput!): ReportItemResult!
|
||||
revokeApiKey(id: ID!): RevokeApiKeyResult!
|
||||
saveArticleReadingProgress(input: SaveArticleReadingProgressInput!): SaveArticleReadingProgressResult!
|
||||
saveFile(input: SaveFileInput!): SaveResult!
|
||||
savePage(input: SavePageInput!): SaveResult!
|
||||
|
|
@ -989,6 +974,22 @@ enum ReportType {
|
|||
SPAM
|
||||
}
|
||||
|
||||
type RevokeApiKeyError {
|
||||
errorCodes: [RevokeApiKeyErrorCode!]!
|
||||
}
|
||||
|
||||
enum RevokeApiKeyErrorCode {
|
||||
BAD_REQUEST
|
||||
NOT_FOUND
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union RevokeApiKeyResult = RevokeApiKeyError | RevokeApiKeySuccess
|
||||
|
||||
type RevokeApiKeySuccess {
|
||||
apiKey: ApiKey!
|
||||
}
|
||||
|
||||
type SaveArticleReadingProgressError {
|
||||
errorCodes: [SaveArticleReadingProgressErrorCode!]!
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ import {
|
|||
ApiKeysError,
|
||||
ApiKeysErrorCode,
|
||||
ApiKeysSuccess,
|
||||
DeleteApiKeyError,
|
||||
DeleteApiKeyErrorCode,
|
||||
DeleteApiKeySuccess,
|
||||
GenerateApiKeyError,
|
||||
GenerateApiKeyErrorCode,
|
||||
GenerateApiKeySuccess,
|
||||
MutationDeleteApiKeyArgs,
|
||||
MutationGenerateApiKeyArgs,
|
||||
MutationRevokeApiKeyArgs,
|
||||
RevokeApiKeyError,
|
||||
RevokeApiKeyErrorCode,
|
||||
RevokeApiKeySuccess,
|
||||
} from '../../generated/graphql'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { env } from '../../env'
|
||||
|
|
@ -32,6 +32,7 @@ export const apiKeysResolver = authorized<ApiKeysSuccess, ApiKeysError>(
|
|||
}
|
||||
|
||||
const apiKeys = await getRepository(ApiKey).find({
|
||||
select: ['id', 'name', 'scopes', 'expiresAt', 'createdAt', 'usedAt'],
|
||||
where: { user: { id: uid } },
|
||||
order: { usedAt: 'DESC', createdAt: 'DESC' },
|
||||
})
|
||||
|
|
@ -75,7 +76,7 @@ export const generateApiKeyResolver = authorized<
|
|||
|
||||
const exp = new Date(expiresAt)
|
||||
const apiKey = generateApiKey()
|
||||
await getRepository(ApiKey).save({
|
||||
const apiKeyData = await getRepository(ApiKey).save({
|
||||
user: { id: uid },
|
||||
name,
|
||||
key: hashApiKey(apiKey),
|
||||
|
|
@ -92,7 +93,12 @@ export const generateApiKeyResolver = authorized<
|
|||
},
|
||||
})
|
||||
|
||||
return { apiKey }
|
||||
return {
|
||||
apiKey: {
|
||||
...apiKeyData,
|
||||
key: apiKey,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
|
|
@ -100,18 +106,18 @@ export const generateApiKeyResolver = authorized<
|
|||
}
|
||||
})
|
||||
|
||||
export const deleteApiKeyResolver = authorized<
|
||||
DeleteApiKeySuccess,
|
||||
DeleteApiKeyError,
|
||||
MutationDeleteApiKeyArgs
|
||||
export const revokeApiKeyResolver = authorized<
|
||||
RevokeApiKeySuccess,
|
||||
RevokeApiKeyError,
|
||||
MutationRevokeApiKeyArgs
|
||||
>(async (_, { id }, { claims: { uid }, log }) => {
|
||||
log.info('deleteApiKeyResolver')
|
||||
log.info('RevokeApiKeyResolver')
|
||||
|
||||
try {
|
||||
const user = await getRepository(User).findOneBy({ id: uid })
|
||||
if (!user) {
|
||||
return {
|
||||
errorCodes: [DeleteApiKeyErrorCode.Unauthorized],
|
||||
errorCodes: [RevokeApiKeyErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,27 +127,30 @@ export const deleteApiKeyResolver = authorized<
|
|||
})
|
||||
if (!apiKey) {
|
||||
return {
|
||||
errorCodes: [DeleteApiKeyErrorCode.NotFound],
|
||||
errorCodes: [RevokeApiKeyErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
|
||||
if (apiKey.user.id !== uid) {
|
||||
return {
|
||||
errorCodes: [DeleteApiKeyErrorCode.Unauthorized],
|
||||
errorCodes: [RevokeApiKeyErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
|
||||
const deletedApiKey = await getRepository(ApiKey).remove(apiKey)
|
||||
deletedApiKey.id = id
|
||||
|
||||
return {
|
||||
apiKey: deletedApiKey,
|
||||
apiKey: {
|
||||
...deletedApiKey,
|
||||
id,
|
||||
key: null,
|
||||
},
|
||||
}
|
||||
} catch (e) {
|
||||
log.error(e)
|
||||
|
||||
return {
|
||||
errorCodes: [DeleteApiKeyErrorCode.BadRequest],
|
||||
errorCodes: [RevokeApiKeyErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import {
|
|||
createLabelResolver,
|
||||
createNewsletterEmailResolver,
|
||||
createReminderResolver,
|
||||
deleteApiKeyResolver,
|
||||
deleteHighlightResolver,
|
||||
deleteLabelResolver,
|
||||
deleteNewsletterEmailResolver,
|
||||
|
|
@ -56,6 +55,7 @@ import {
|
|||
newsletterEmailsResolver,
|
||||
reminderResolver,
|
||||
reportItemResolver,
|
||||
revokeApiKeyResolver,
|
||||
saveArticleReadingProgressResolver,
|
||||
saveFileResolver,
|
||||
savePageResolver,
|
||||
|
|
@ -160,7 +160,7 @@ export const functionResolvers = {
|
|||
addPopularRead: addPopularReadResolver,
|
||||
setWebhook: setWebhookResolver,
|
||||
deleteWebhook: deleteWebhookResolver,
|
||||
deleteApiKey: deleteApiKeyResolver,
|
||||
revokeApiKey: revokeApiKeyResolver,
|
||||
},
|
||||
Query: {
|
||||
me: getMeUserResolver,
|
||||
|
|
@ -581,5 +581,5 @@ export const functionResolvers = {
|
|||
...resultResolveTypeResolver('DeleteWebhook'),
|
||||
...resultResolveTypeResolver('Webhook'),
|
||||
...resultResolveTypeResolver('ApiKeys'),
|
||||
...resultResolveTypeResolver('DeleteApiKey'),
|
||||
...resultResolveTypeResolver('RevokeApiKey'),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1420,7 +1420,7 @@ const schema = gql`
|
|||
union GenerateApiKeyResult = GenerateApiKeySuccess | GenerateApiKeyError
|
||||
|
||||
type GenerateApiKeySuccess {
|
||||
apiKey: String!
|
||||
apiKey: ApiKey!
|
||||
}
|
||||
|
||||
type GenerateApiKeyError {
|
||||
|
|
@ -1679,6 +1679,7 @@ const schema = gql`
|
|||
type ApiKey {
|
||||
id: ID!
|
||||
name: String!
|
||||
key: String
|
||||
scopes: [String!]
|
||||
createdAt: Date!
|
||||
expiresAt: Date!
|
||||
|
|
@ -1694,17 +1695,17 @@ const schema = gql`
|
|||
BAD_REQUEST
|
||||
}
|
||||
|
||||
union DeleteApiKeyResult = DeleteApiKeySuccess | DeleteApiKeyError
|
||||
union RevokeApiKeyResult = RevokeApiKeySuccess | RevokeApiKeyError
|
||||
|
||||
type DeleteApiKeySuccess {
|
||||
type RevokeApiKeySuccess {
|
||||
apiKey: ApiKey!
|
||||
}
|
||||
|
||||
type DeleteApiKeyError {
|
||||
errorCodes: [DeleteApiKeyErrorCode!]!
|
||||
type RevokeApiKeyError {
|
||||
errorCodes: [RevokeApiKeyErrorCode!]!
|
||||
}
|
||||
|
||||
enum DeleteApiKeyErrorCode {
|
||||
enum RevokeApiKeyErrorCode {
|
||||
UNAUTHORIZED
|
||||
BAD_REQUEST
|
||||
NOT_FOUND
|
||||
|
|
@ -1777,7 +1778,7 @@ const schema = gql`
|
|||
addPopularRead(name: String!): AddPopularReadResult!
|
||||
setWebhook(input: SetWebhookInput!): SetWebhookResult!
|
||||
deleteWebhook(id: ID!): DeleteWebhookResult!
|
||||
deleteApiKey(id: ID!): DeleteApiKeyResult!
|
||||
revokeApiKey(id: ID!): RevokeApiKeyResult!
|
||||
}
|
||||
|
||||
# FIXME: remove sort from feedArticles after all cached tabs are closed
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const testAPIKey = (apiKey: string): supertest.Test => {
|
|||
return graphqlRequest(query, apiKey)
|
||||
}
|
||||
|
||||
describe('generate api key', () => {
|
||||
describe('Api Key resolver', () => {
|
||||
const username = 'fake_user'
|
||||
|
||||
let authToken: string
|
||||
|
|
@ -46,15 +46,18 @@ describe('generate api key', () => {
|
|||
await deleteTestUser(username)
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
query = `
|
||||
describe('generate api key', () => {
|
||||
beforeEach(() => {
|
||||
query = `
|
||||
mutation {
|
||||
generateApiKey(input: {
|
||||
name: "${name}"
|
||||
expiresAt: "${expiresAt}"
|
||||
}) {
|
||||
... on GenerateApiKeySuccess {
|
||||
apiKey
|
||||
apiKey {
|
||||
key
|
||||
}
|
||||
}
|
||||
... on GenerateApiKeyError {
|
||||
errorCodes
|
||||
|
|
@ -62,33 +65,92 @@ describe('generate api key', () => {
|
|||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
context('when api key is not expired', () => {
|
||||
before(() => {
|
||||
name = 'test'
|
||||
expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString()
|
||||
})
|
||||
|
||||
it('should generate an api key', async () => {
|
||||
const response = await graphqlRequest(query, authToken)
|
||||
expect(response.body.data.generateApiKey.apiKey).to.be.a('string')
|
||||
context('when api key is not expired', () => {
|
||||
before(() => {
|
||||
name = 'test'
|
||||
expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString()
|
||||
})
|
||||
|
||||
return testAPIKey(response.body.data.generateApiKey.apiKey).expect(200)
|
||||
it('should generate an api key', async () => {
|
||||
const response = await graphqlRequest(query, authToken).expect(200)
|
||||
expect(response.body.data.generateApiKey.apiKey.key).to.be.a('string')
|
||||
|
||||
return testAPIKey(response.body.data.generateApiKey.apiKey.key).expect(
|
||||
200
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
context('when api key is expired', () => {
|
||||
before(() => {
|
||||
name = 'test-expired'
|
||||
expiresAt = new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString()
|
||||
})
|
||||
|
||||
it('should generate an expired api key', async () => {
|
||||
const response = await graphqlRequest(query, authToken).expect(200)
|
||||
expect(response.body.data.generateApiKey.apiKey.key).to.be.a('string')
|
||||
|
||||
return testAPIKey(response.body.data.generateApiKey.apiKey.key).expect(
|
||||
500
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('when api key is expired', () => {
|
||||
before(() => {
|
||||
name = 'test-expired'
|
||||
expiresAt = new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString()
|
||||
describe('revoke api key', () => {
|
||||
let apiKey: string
|
||||
let apiKeyId: string
|
||||
|
||||
before(async () => {
|
||||
query = `
|
||||
mutation {
|
||||
generateApiKey(input: {
|
||||
name: "test-revoke"
|
||||
expiresAt: "${new Date(
|
||||
Date.now() + 1000 * 60 * 60 * 24
|
||||
).toISOString()}"
|
||||
}) {
|
||||
... on GenerateApiKeySuccess {
|
||||
apiKey {
|
||||
id
|
||||
key
|
||||
}
|
||||
}
|
||||
... on GenerateApiKeyError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const response = await graphqlRequest(query, authToken)
|
||||
apiKey = response.body.data.generateApiKey.apiKey.key
|
||||
apiKeyId = response.body.data.generateApiKey.apiKey.id
|
||||
})
|
||||
|
||||
it('should generate an expired api key', async () => {
|
||||
const response = await graphqlRequest(query, authToken)
|
||||
expect(response.body.data.generateApiKey.apiKey).to.be.a('string')
|
||||
it('should revoke an api key', async () => {
|
||||
query = `
|
||||
mutation {
|
||||
revokeApiKey(id: "${apiKeyId}") {
|
||||
... on RevokeApiKeySuccess {
|
||||
apiKey {
|
||||
id
|
||||
}
|
||||
}
|
||||
... on RevokeApiKeyError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
return testAPIKey(response.body.data.generateApiKey.apiKey).expect(500)
|
||||
const response = await graphqlRequest(query, authToken).expect(200)
|
||||
expect(response.body.data.revokeApiKey.apiKey.id).to.be.a('string')
|
||||
|
||||
return testAPIKey(apiKey).expect(500)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue