From 1ba6049991bc1bcaef15a803352e9a2dde32015c Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Tue, 18 May 2021 16:01:34 -0500 Subject: [PATCH] Login endpoint --- framework/bigcommerce/api/index.ts | 3 +- framework/bigcommerce/api/login/index.ts | 10 +++++ framework/bigcommerce/api/login/login.ts | 49 +++++++++++++++++++++++ framework/bigcommerce/types/login.ts | 1 + framework/commerce/api/endpoints/login.ts | 35 ++++++++++++++++ framework/commerce/api/index.ts | 3 +- framework/commerce/types/login.ts | 20 +++++++++ framework/commerce/utils/types.ts | 2 +- pages/api/login.ts | 8 ++++ 9 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 framework/bigcommerce/api/login/index.ts create mode 100644 framework/bigcommerce/api/login/login.ts create mode 100644 framework/bigcommerce/types/login.ts create mode 100644 framework/commerce/api/endpoints/login.ts create mode 100644 framework/commerce/types/login.ts create mode 100644 pages/api/login.ts diff --git a/framework/bigcommerce/api/index.ts b/framework/bigcommerce/api/index.ts index e30517171..425759df5 100644 --- a/framework/bigcommerce/api/index.ts +++ b/framework/bigcommerce/api/index.ts @@ -11,6 +11,7 @@ import fetchStoreApi from './utils/fetch-store-api' import type { CartAPI } from './cart' import type { CustomerAPI } from './customer' +import type { LoginAPI } from './login' import login from './operations/login' export interface BigcommerceConfig extends CommerceAPIConfig { @@ -112,7 +113,7 @@ export const provider = { export type Provider = typeof provider -export type APIs = CartAPI | CustomerAPI +export type APIs = CartAPI | CustomerAPI | LoginAPI export type BigcommerceAPI

= CommerceAPI

diff --git a/framework/bigcommerce/api/login/index.ts b/framework/bigcommerce/api/login/index.ts new file mode 100644 index 000000000..8b9543c29 --- /dev/null +++ b/framework/bigcommerce/api/login/index.ts @@ -0,0 +1,10 @@ +import type { GetAPISchema } from '@commerce/api' +import type { LoginSchema } from '../../types/login' +import type { BigcommerceAPI } from '..' +import login from './login' + +export type LoginAPI = GetAPISchema + +export type LoginEndpoint = LoginAPI['endpoint'] + +export const operations = { login } diff --git a/framework/bigcommerce/api/login/login.ts b/framework/bigcommerce/api/login/login.ts new file mode 100644 index 000000000..670734963 --- /dev/null +++ b/framework/bigcommerce/api/login/login.ts @@ -0,0 +1,49 @@ +import { FetcherError } from '@commerce/utils/errors' +import type { LoginEndpoint } from '.' + +const invalidCredentials = /invalid credentials/i + +const login: LoginEndpoint['operations']['login'] = async ({ + res, + body: { email, password }, + config, + commerce, +}) => { + // TODO: Add proper validations with something like Ajv + if (!(email && password)) { + return res.status(400).json({ + data: null, + errors: [{ message: 'Invalid request' }], + }) + } + // TODO: validate the password and email + // Passwords must be at least 7 characters and contain both alphabetic + // and numeric characters. + + try { + await commerce.login({ variables: { email, password }, config, res }) + } catch (error) { + // Check if the email and password didn't match an existing account + if ( + error instanceof FetcherError && + invalidCredentials.test(error.message) + ) { + return res.status(401).json({ + data: null, + errors: [ + { + message: + 'Cannot find an account that matches the provided credentials', + code: 'invalid_credentials', + }, + ], + }) + } + + throw error + } + + res.status(200).json({ data: null }) +} + +export default login diff --git a/framework/bigcommerce/types/login.ts b/framework/bigcommerce/types/login.ts new file mode 100644 index 000000000..3313c60d8 --- /dev/null +++ b/framework/bigcommerce/types/login.ts @@ -0,0 +1 @@ +export * from '@commerce/types/login' diff --git a/framework/commerce/api/endpoints/login.ts b/framework/commerce/api/endpoints/login.ts new file mode 100644 index 000000000..b5b70a1da --- /dev/null +++ b/framework/commerce/api/endpoints/login.ts @@ -0,0 +1,35 @@ +import type { LoginSchema } from '../../types/login' +import { CommerceAPIError } from '../utils/errors' +import isAllowedOperation from '../utils/is-allowed-operation' +import type { GetAPISchema } from '..' + +const loginEndpoint: GetAPISchema< + any, + LoginSchema +>['endpoint']['handler'] = async (ctx) => { + const { req, res, operations } = ctx + + if ( + !isAllowedOperation(req, res, { + POST: operations['login'], + }) + ) { + return + } + + try { + const body = req.body ?? {} + return await operations['login']({ ...ctx, body }) + } catch (error) { + console.error(error) + + const message = + error instanceof CommerceAPIError + ? 'An unexpected error ocurred with the Commerce API' + : 'An unexpected error ocurred' + + res.status(500).json({ data: null, errors: [{ message }] }) + } +} + +export default loginEndpoint diff --git a/framework/commerce/api/index.ts b/framework/commerce/api/index.ts index f076657c0..86ac17e43 100644 --- a/framework/commerce/api/index.ts +++ b/framework/commerce/api/index.ts @@ -3,6 +3,7 @@ import type { RequestInit, Response } from '@vercel/fetch' import type { APIEndpoint, APIHandler } from './utils/types' import type { CartSchema } from '../types/cart' import type { CustomerSchema } from '../types/customer' +import type { LoginSchema } from '../types/login' import { defaultOperations, OPERATIONS, @@ -10,7 +11,7 @@ import { APIOperations, } from './operations' -export type APISchemas = CartSchema | CustomerSchema +export type APISchemas = CartSchema | CustomerSchema | LoginSchema export type GetAPISchema< C extends CommerceAPI, diff --git a/framework/commerce/types/login.ts b/framework/commerce/types/login.ts new file mode 100644 index 000000000..5974ed2db --- /dev/null +++ b/framework/commerce/types/login.ts @@ -0,0 +1,20 @@ +export type LoginBody = { + email: string + password: string +} + +export type LoginTypes = { + body: LoginBody +} + +export type LoginSchema = { + endpoint: { + options: {} + operations: { + login: { + data: null + body: T['body'] + } + } + } +} diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 6ef673d77..93fd8153c 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -71,7 +71,7 @@ export type HookSchemaBase = { data: any // Input expected by the hook input: {} - // Input expected before doing a fetch operation + // Input expected before doing a fetch operation (aka fetch handler) fetchInput?: {} // Data expected by the fetch operation body?: {} diff --git a/pages/api/login.ts b/pages/api/login.ts new file mode 100644 index 000000000..5f87a24e8 --- /dev/null +++ b/pages/api/login.ts @@ -0,0 +1,8 @@ +import login from '@commerce/api/endpoints/login' +import { LoginAPI, operations } from '@framework/api/login' +import commerce from '@lib/api/commerce' + +export default commerce.endpoint({ + handler: login as LoginAPI['endpoint']['handler'], + operations, +})