@labdigital/commercetools-mock 1.11.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@labdigital/commercetools-mock",
3
3
  "author": "Michael van Tellingen",
4
- "version": "1.11.0",
4
+ "version": "2.1.0",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.js",
@@ -19,7 +19,7 @@
19
19
  }
20
20
  },
21
21
  "engines": {
22
- "node": ">=16",
22
+ "node": ">=18",
23
23
  "pnpm": ">=8.6.5"
24
24
  },
25
25
  "packageManager": "pnpm@8.6.5",
@@ -39,7 +39,7 @@
39
39
  "express": "^4.18.2",
40
40
  "lodash.isequal": "^4.5.0",
41
41
  "morgan": "^1.10.0",
42
- "nock": "^13.3.2",
42
+ "msw": "^2.0.0",
43
43
  "supertest": "^6.3.3",
44
44
  "uuid": "^9.0.0"
45
45
  },
package/src/constants.ts CHANGED
@@ -1,4 +1,2 @@
1
- export const DEFAULT_API_HOSTNAME =
2
- /^https:\/\/api\..*?\.commercetools.com:443$/
3
- export const DEFAULT_AUTH_HOSTNAME =
4
- /^https:\/\/auth\..*?\.commercetools.com:443$/
1
+ export const DEFAULT_API_HOSTNAME = 'https://api.*.commercetools.com'
2
+ export const DEFAULT_AUTH_HOSTNAME = 'https://auth.*.commercetools.com'
package/src/ctMock.ts CHANGED
@@ -1,7 +1,8 @@
1
- import nock from 'nock'
2
1
  import express, { NextFunction, Request, Response } from 'express'
3
2
  import supertest from 'supertest'
4
3
  import morgan from 'morgan'
4
+ import { setupServer, SetupServer } from 'msw/node'
5
+ import { http, HttpResponse } from 'msw'
5
6
  import { AbstractStorage, InMemoryStorage } from './storage/index.js'
6
7
  import { Services } from './types.js'
7
8
  import { CommercetoolsError } from './exceptions.js'
@@ -36,16 +37,15 @@ const DEFAULT_OPTIONS: CommercetoolsMockOptions = {
36
37
  silent: false,
37
38
  }
38
39
 
40
+ const _globalListeners: SetupServer[] = []
41
+
39
42
  export class CommercetoolsMock {
40
43
  public app: express.Express
41
44
  public options: CommercetoolsMockOptions
42
45
 
43
46
  private _storage: AbstractStorage
44
47
  private _oauth2: OAuth2Server
45
- private _nockScopes: {
46
- auth: nock.Scope | undefined
47
- api: nock.Scope | undefined
48
- } = { auth: undefined, api: undefined }
48
+ private _mswServer: SetupServer | undefined = undefined
49
49
  private _services: Services | null
50
50
  private _repositories: RepositoryMap | null
51
51
  private _projectService?: ProjectService
@@ -67,19 +67,17 @@ export class CommercetoolsMock {
67
67
 
68
68
  start() {
69
69
  // Order is important here when the hostnames match
70
- this.mockAuthHost()
71
- this.mockApiHost()
70
+ this.clear()
71
+ this.startServer()
72
72
  }
73
73
 
74
74
  stop() {
75
- this._nockScopes.auth?.persist(false)
76
- this._nockScopes.auth = undefined
77
-
78
- this._nockScopes.api?.persist(false)
79
- this._nockScopes.api = undefined
75
+ this._mswServer?.close()
76
+ this._mswServer = undefined
80
77
  }
81
78
 
82
79
  clear() {
80
+ this._mswServer?.resetHandlers()
83
81
  this._storage.clear()
84
82
  }
85
83
 
@@ -108,6 +106,7 @@ export class CommercetoolsMock {
108
106
 
109
107
  private createApp(options?: AppOptions): express.Express {
110
108
  this._repositories = createRepositories(this._storage)
109
+ this._oauth2.setCustomerRepository(this._repositories.customer)
111
110
 
112
111
  const app = express()
113
112
 
@@ -141,6 +140,14 @@ export class CommercetoolsMock {
141
140
 
142
141
  app.use((err: Error, req: Request, resp: Response, next: NextFunction) => {
143
142
  if (err instanceof CommercetoolsError) {
143
+ if (err.errors?.length > 0) {
144
+ return resp.status(err.statusCode).send({
145
+ statusCode: err.statusCode,
146
+ message: err.message,
147
+ errors: err.errors,
148
+ })
149
+ }
150
+
144
151
  return resp.status(err.statusCode).send({
145
152
  statusCode: err.statusCode,
146
153
  message: err.message,
@@ -157,48 +164,81 @@ export class CommercetoolsMock {
157
164
  return app
158
165
  }
159
166
 
160
- private mockApiHost() {
161
- const app = this.app
167
+ private startServer() {
168
+ // Check if there are any other servers running
169
+ if (_globalListeners.length > 0) {
170
+ if (this._mswServer !== undefined) {
171
+ throw new Error('Server already started')
172
+ } else {
173
+ console.warn("Server wasn't stopped properly, clearing")
174
+ _globalListeners.forEach((listener) => listener.close())
175
+ }
176
+ }
162
177
 
163
- this._nockScopes.api = nock(this.options.apiHost)
164
- .persist()
165
- .get(/.*/)
166
- .reply(async function (uri) {
167
- const response = await supertest(app)
168
- .get(uri)
169
- .set(copyHeaders(this.req.headers))
170
- return [response.status, response.body]
171
- })
172
- .post(/.*/)
173
- .reply(async function (uri, body) {
174
- const response = await supertest(app)
175
- .post(uri)
176
- .set(copyHeaders(this.req.headers))
178
+ const app = this.app
179
+ this._mswServer = setupServer(
180
+ http.post(`${this.options.authHost}/oauth/*`, async ({ request }) => {
181
+ const text = await request.text()
182
+ const url = new URL(request.url)
183
+ const res = await supertest(app)
184
+ .post(url.pathname + '?' + url.searchParams.toString())
185
+ .set(copyHeaders(request.headers))
186
+ .send(text)
187
+
188
+ return new HttpResponse(res.text, {
189
+ status: res.status,
190
+ headers: res.headers,
191
+ })
192
+ }),
193
+ http.get(`${this.options.apiHost}/*`, async ({ request }) => {
194
+ const body = await request.text()
195
+ const url = new URL(request.url)
196
+ const res = await supertest(app)
197
+ .get(url.pathname + '?' + url.searchParams.toString())
198
+ .set(copyHeaders(request.headers))
177
199
  .send(body)
178
- return [response.status, response.body]
179
- })
180
- .delete(/.*/)
181
- .reply(async function (uri, body) {
182
- const response = await supertest(app)
183
- .delete(uri)
184
- .set(copyHeaders(this.req.headers))
200
+ return new HttpResponse(res.text, {
201
+ status: res.status,
202
+ headers: res.headers,
203
+ })
204
+ }),
205
+ http.post(`${this.options.apiHost}/*`, async ({ request }) => {
206
+ const body = await request.text()
207
+ const url = new URL(request.url)
208
+ const res = await supertest(app)
209
+ .post(url.pathname + '?' + url.searchParams.toString())
210
+ .set(copyHeaders(request.headers))
211
+ .send(body)
212
+ return new HttpResponse(res.text, {
213
+ status: res.status,
214
+ headers: res.headers,
215
+ })
216
+ }),
217
+ http.delete(`${this.options.apiHost}/*`, async ({ request }) => {
218
+ const body = await request.text()
219
+ const url = new URL(request.url)
220
+ const res = await supertest(app)
221
+ .delete(url.pathname + '?' + url.searchParams.toString())
222
+ .set(copyHeaders(request.headers))
185
223
  .send(body)
186
- return [response.status, response.body]
187
- })
188
- }
189
-
190
- private mockAuthHost() {
191
- const app = this.app
192
224
 
193
- this._nockScopes.auth = nock(this.options.authHost)
194
- .persist()
195
- .post(/^\/oauth\/.*/)
196
- .reply(async function (uri, body) {
197
- const response = await supertest(app)
198
- .post(uri + '?' + body)
199
- .set(copyHeaders(this.req.headers))
200
- .send()
201
- return [response.status, response.body]
225
+ return new HttpResponse(res.text, {
226
+ status: res.status,
227
+ headers: res.headers,
228
+ })
202
229
  })
230
+ )
231
+ this._mswServer.listen({
232
+ // We need to allow requests done by supertest
233
+ onUnhandledRequest: (request, print) => {
234
+ const url = new URL(request.url)
235
+ if (url.hostname === '127.0.0.1') {
236
+ return
237
+ }
238
+ print.error()
239
+ },
240
+ })
241
+
242
+ _globalListeners.push(this._mswServer)
203
243
  }
204
244
  }
package/src/exceptions.ts CHANGED
@@ -1,15 +1,19 @@
1
1
  export abstract class BaseError {
2
2
  abstract message: string
3
+ abstract errors?: BaseError[]
3
4
  }
4
5
 
5
6
  export class CommercetoolsError<T extends BaseError> extends Error {
6
7
  info: T
7
8
  statusCode: number
8
9
 
10
+ errors: BaseError[]
11
+
9
12
  constructor(info: T, statusCode = 400) {
10
13
  super(info.message)
11
14
  this.info = info
12
15
  this.statusCode = statusCode || 500
16
+ this.errors = info.errors ?? []
13
17
  }
14
18
  }
15
19
 
package/src/index.test.ts CHANGED
@@ -1,27 +1,63 @@
1
1
  import { type InvalidTokenError } from '@commercetools/platform-sdk'
2
2
  import { CommercetoolsMock } from './index.js'
3
- import { afterEach, beforeEach, expect, test } from 'vitest'
4
- import nock from 'nock'
3
+ import { expect, test } from 'vitest'
5
4
  import got from 'got'
6
5
 
7
- beforeEach(() => {
8
- nock.disableNetConnect()
9
- nock.enableNetConnect('127.0.0.1') // supertest
10
- })
11
- afterEach(() => {
12
- nock.enableNetConnect()
13
- nock.cleanAll()
6
+ test('node:fetch client', async () => {
7
+ const ctMock = new CommercetoolsMock({
8
+ enableAuthentication: true,
9
+ validateCredentials: true,
10
+ apiHost: 'https://localhost',
11
+ authHost: 'https://localhost:8080',
12
+ })
13
+ ctMock.start()
14
+
15
+ const authHeader = 'Basic ' + Buffer.from('foo:bar').toString('base64')
16
+ let response = await fetch('https://localhost:8080/oauth/token', {
17
+ method: 'POST',
18
+ headers: {
19
+ 'Content-Type': 'application/x-www-form-urlencoded',
20
+ Authorization: authHeader,
21
+ },
22
+ body: new URLSearchParams({
23
+ grant_type: 'client_credentials',
24
+ scope: 'manage_project:commercetools-node-mock',
25
+ }),
26
+ })
27
+
28
+ const authBody = await response.json()
29
+ expect(response.status).toBe(200)
30
+
31
+ const token = authBody.access_token
32
+ response = await fetch('https://localhost/my-project/orders', {
33
+ headers: {
34
+ Authorization: `Bearer ${token}`,
35
+ },
36
+ })
37
+
38
+ const body = await response.json()
39
+ expect(response.status).toBe(200)
40
+ expect(body).toStrictEqual({
41
+ count: 0,
42
+ total: 0,
43
+ offset: 0,
44
+ limit: 20,
45
+ results: [],
46
+ })
47
+ ctMock.stop()
14
48
  })
15
49
 
16
- test('Default mock endpoints', async () => {
50
+ test('got client', async () => {
17
51
  const ctMock = new CommercetoolsMock({
18
52
  enableAuthentication: true,
19
53
  validateCredentials: true,
54
+ apiHost: 'https://localhost',
55
+ authHost: 'https://localhost:8080',
20
56
  })
21
57
  ctMock.start()
22
58
 
23
59
  let response = await got.post<{ access_token: string }>(
24
- 'https://auth.europe-west1.gcp.commercetools.com/oauth/token',
60
+ 'https://localhost:8080/oauth/token',
25
61
  {
26
62
  searchParams: {
27
63
  grant_type: 'client_credentials',
@@ -36,15 +72,12 @@ test('Default mock endpoints', async () => {
36
72
 
37
73
  const token = response.body.access_token
38
74
  expect(response.body.access_token).toBeDefined()
39
- response = await got.get(
40
- 'https://api.europe-west1.gcp.commercetools.com/my-project/orders',
41
- {
42
- headers: {
43
- Authorization: `Bearer ${token}`,
44
- },
45
- responseType: 'json',
46
- }
47
- )
75
+ response = await got.get('https://localhost/my-project/orders', {
76
+ headers: {
77
+ Authorization: `Bearer ${token}`,
78
+ },
79
+ responseType: 'json',
80
+ })
48
81
  expect(response.statusCode).toBe(200)
49
82
  expect(response.body).toStrictEqual({
50
83
  count: 0,
@@ -192,6 +225,7 @@ test('apiHost mock proxy: querystring', async () => {
192
225
  expand: 'custom.type',
193
226
  },
194
227
  })
228
+
195
229
  expect(response.statusCode).toBe(200)
196
230
  expect(response.body).toStrictEqual({
197
231
  count: 0,
@@ -0,0 +1,7 @@
1
+ export const validatePassword = (
2
+ clearPassword: string,
3
+ hashedPassword: string
4
+ ) => hashPassword(clearPassword) === hashedPassword
5
+
6
+ export const hashPassword = (clearPassword: string) =>
7
+ Buffer.from(clearPassword).toString('base64')
package/src/lib/proxy.ts CHANGED
@@ -1,12 +1,12 @@
1
- export const copyHeaders = (headers: Record<string, string>) => {
2
- const validHeaders = ['accept', 'host', 'authorization']
1
+ export const copyHeaders = (headers: Headers) => {
2
+ const validHeaders = ['accept', 'host', 'authorization', 'content-type']
3
3
  const result: Record<string, string> = {}
4
4
 
5
- Object.entries(headers).forEach(([key, value]) => {
5
+ for (const [key, value] of headers.entries()) {
6
6
  if (validHeaders.includes(key.toLowerCase())) {
7
7
  result[key] = value
8
8
  }
9
- })
9
+ }
10
10
 
11
11
  return result
12
12
  }
@@ -10,18 +10,45 @@ import { CommercetoolsError, InvalidRequestError } from '../exceptions.js'
10
10
  import { InvalidClientError, UnsupportedGrantType } from './errors.js'
11
11
  import { OAuth2Store } from './store.js'
12
12
  import { getBearerToken } from './helpers.js'
13
+ import { CustomerRepository } from '../repositories/customer.js'
14
+ import { hashPassword } from '../lib/password.js'
15
+
16
+ type AuthRequest = Request & {
17
+ credentials: {
18
+ clientId: string
19
+ clientSecret: string
20
+ }
21
+ }
13
22
 
14
23
  export class OAuth2Server {
15
24
  store: OAuth2Store
25
+ private customerRepository: CustomerRepository
16
26
 
17
27
  constructor(options: { enabled: boolean; validate: boolean }) {
18
28
  this.store = new OAuth2Store(options.validate)
19
29
  }
20
30
 
31
+ setCustomerRepository(repository: CustomerRepository) {
32
+ this.customerRepository = repository
33
+ }
34
+
21
35
  createRouter() {
22
36
  const router = express.Router()
23
37
  router.use(bodyParser.urlencoded({ extended: true }))
38
+ router.use(this.validateClientCredentials.bind(this))
24
39
  router.post('/token', this.tokenHandler.bind(this))
40
+ router.post(
41
+ '/:projectKey/customers/token',
42
+ this.customerTokenHandler.bind(this)
43
+ )
44
+ router.post(
45
+ '/:projectKey/in-store/key=:storeKey/customers/token',
46
+ this.inStoreCustomerTokenHandler.bind(this)
47
+ )
48
+ router.post(
49
+ '/:projectKey/anonymous/token',
50
+ this.anonymousTokenHandler.bind(this)
51
+ )
25
52
  return router
26
53
  }
27
54
 
@@ -56,7 +83,12 @@ export class OAuth2Server {
56
83
  next()
57
84
  }
58
85
  }
59
- async tokenHandler(request: Request, response: Response, next: NextFunction) {
86
+
87
+ async validateClientCredentials(
88
+ request: AuthRequest,
89
+ response: Response,
90
+ next: NextFunction
91
+ ) {
60
92
  const authHeader = request.header('Authorization')
61
93
  if (!authHeader) {
62
94
  return next(
@@ -84,6 +116,19 @@ export class OAuth2Server {
84
116
  )
85
117
  }
86
118
 
119
+ request.credentials = {
120
+ clientId: credentials.name,
121
+ clientSecret: credentials.pass,
122
+ }
123
+
124
+ next()
125
+ }
126
+
127
+ async tokenHandler(
128
+ request: AuthRequest,
129
+ response: Response,
130
+ next: NextFunction
131
+ ) {
87
132
  const grantType = request.query.grant_type || request.body.grant_type
88
133
  if (!grantType) {
89
134
  return next(
@@ -99,8 +144,15 @@ export class OAuth2Server {
99
144
 
100
145
  if (grantType === 'client_credentials') {
101
146
  const token = this.store.getClientToken(
102
- credentials.name,
103
- credentials.pass,
147
+ request.credentials.clientId,
148
+ request.credentials.clientSecret,
149
+ request.query.scope?.toString()
150
+ )
151
+ return response.status(200).send(token)
152
+ } else if (grantType === 'refresh_token') {
153
+ const token = this.store.getClientToken(
154
+ request.credentials.clientId,
155
+ request.credentials.clientSecret,
104
156
  request.query.scope?.toString()
105
157
  )
106
158
  return response.status(200).send(token)
@@ -116,4 +168,86 @@ export class OAuth2Server {
116
168
  )
117
169
  }
118
170
  }
171
+ async customerTokenHandler(
172
+ request: AuthRequest,
173
+ response: Response,
174
+ next: NextFunction
175
+ ) {
176
+ const grantType = request.query.grant_type || request.body.grant_type
177
+ if (!grantType) {
178
+ return next(
179
+ new CommercetoolsError<InvalidRequestError>(
180
+ {
181
+ code: 'invalid_request',
182
+ message: 'Missing required parameter: grant_type.',
183
+ },
184
+ 400
185
+ )
186
+ )
187
+ }
188
+
189
+ if (grantType === 'password') {
190
+ const username = request.query.username || request.body.username
191
+ const password = hashPassword(
192
+ request.query.password || request.body.password
193
+ )
194
+ const scope =
195
+ request.query.scope?.toString() || request.body.scope?.toString()
196
+
197
+ const result = this.customerRepository.query(
198
+ { projectKey: request.params.projectKey },
199
+ {
200
+ where: [`email = "${username}"`, `password = "${password}"`],
201
+ }
202
+ )
203
+
204
+ if (result.count === 0) {
205
+ return next(
206
+ new CommercetoolsError<any>(
207
+ {
208
+ code: 'invalid_customer_account_credentials',
209
+ message: 'Customer account with the given credentials not found.',
210
+ },
211
+ 400
212
+ )
213
+ )
214
+ }
215
+
216
+ const customer = result.results[0]
217
+ const token = this.store.getCustomerToken(scope, customer.id)
218
+ return response.status(200).send(token)
219
+ }
220
+ }
221
+
222
+ async inStoreCustomerTokenHandler(
223
+ request: Request,
224
+ response: Response,
225
+ next: NextFunction
226
+ ) {
227
+ return next(
228
+ new CommercetoolsError<InvalidClientError>(
229
+ {
230
+ code: 'invalid_client',
231
+ message: 'Not implemented yet in commercetools-mock',
232
+ },
233
+ 401
234
+ )
235
+ )
236
+ }
237
+
238
+ async anonymousTokenHandler(
239
+ request: Request,
240
+ response: Response,
241
+ next: NextFunction
242
+ ) {
243
+ return next(
244
+ new CommercetoolsError<InvalidClientError>(
245
+ {
246
+ code: 'invalid_client',
247
+ message: 'Not implemented yet in commercetools-mock',
248
+ },
249
+ 401
250
+ )
251
+ )
252
+ }
119
253
  }
@@ -26,6 +26,19 @@ export class OAuth2Store {
26
26
  return token
27
27
  }
28
28
 
29
+ getCustomerToken(scope: string, customerId: string) {
30
+ const token: Token = {
31
+ access_token: randomBytes(16).toString('base64'),
32
+ token_type: 'Bearer',
33
+ expires_in: 172800,
34
+ scope: scope
35
+ ? `${scope} custome_id:${customerId}`
36
+ : `customer_id: ${customerId}`,
37
+ }
38
+ this.tokens.push(token)
39
+ return token
40
+ }
41
+
29
42
  validateToken(token: string) {
30
43
  if (!this.validate) return true
31
44
 
@@ -4,6 +4,7 @@ import type {
4
4
  CustomerDraft,
5
5
  CustomerSetAuthenticationModeAction,
6
6
  CustomerSetCustomFieldAction,
7
+ DuplicateFieldError,
7
8
  InvalidInputError,
8
9
  InvalidJsonInputError,
9
10
  } from '@commercetools/platform-sdk'
@@ -14,6 +15,7 @@ import {
14
15
  AbstractResourceRepository,
15
16
  type RepositoryContext,
16
17
  } from './abstract.js'
18
+ import { hashPassword } from '../lib/password.js'
17
19
 
18
20
  export class CustomerRepository extends AbstractResourceRepository<'customer'> {
19
21
  getTypeId() {
@@ -21,13 +23,32 @@ export class CustomerRepository extends AbstractResourceRepository<'customer'> {
21
23
  }
22
24
 
23
25
  create(context: RepositoryContext, draft: CustomerDraft): Customer {
26
+ // Check uniqueness
27
+ const results = this._storage.query(context.projectKey, this.getTypeId(), {
28
+ where: [`email="${draft.email.toLocaleLowerCase()}"`],
29
+ })
30
+ if (results.count > 0) {
31
+ throw new CommercetoolsError<any>({
32
+ code: 'CustomerAlreadyExists',
33
+ statusCode: 400,
34
+ message:
35
+ 'There is already an existing customer with the provided email.',
36
+ errors: [
37
+ {
38
+ code: 'DuplicateField',
39
+ message: `Customer with email '${draft.email}' already exists.`,
40
+ duplicateValue: draft.email,
41
+ field: 'email',
42
+ } as DuplicateFieldError,
43
+ ],
44
+ })
45
+ }
46
+
24
47
  const resource: Customer = {
25
48
  ...getBaseResourceProperties(),
26
49
  authenticationMode: draft.authenticationMode || 'Password',
27
- email: draft.email,
28
- password: draft.password
29
- ? Buffer.from(draft.password).toString('base64')
30
- : undefined,
50
+ email: draft.email.toLowerCase(),
51
+ password: draft.password ? hashPassword(draft.password) : undefined,
31
52
  isEmailVerified: draft.isEmailVerified || false,
32
53
  addresses: [],
33
54
  }
@@ -36,11 +57,14 @@ export class CustomerRepository extends AbstractResourceRepository<'customer'> {
36
57
  }
37
58
 
38
59
  getMe(context: RepositoryContext): Customer | undefined {
60
+ // grab the first customer you can find for now. In the future we should
61
+ // use the customer id from the scope of the token
39
62
  const results = this._storage.query(
40
63
  context.projectKey,
41
64
  this.getTypeId(),
42
65
  {}
43
- ) // grab the first customer you can find
66
+ )
67
+
44
68
  if (results.count > 0) {
45
69
  return results.results[0] as Customer
46
70
  }
@@ -76,9 +100,7 @@ export class CustomerRepository extends AbstractResourceRepository<'customer'> {
76
100
  return
77
101
  }
78
102
  if (authMode === 'Password') {
79
- resource.password = password
80
- ? Buffer.from(password).toString('base64')
81
- : undefined
103
+ resource.password = password ? hashPassword(password) : undefined
82
104
  return
83
105
  }
84
106
  throw new CommercetoolsError<InvalidJsonInputError>(
@@ -2,6 +2,7 @@ import { Request, Response, Router } from 'express'
2
2
  import { CustomerRepository } from '../repositories/customer.js'
3
3
  import { getRepositoryContext } from '../repositories/helpers.js'
4
4
  import AbstractService from './abstract.js'
5
+ import { hashPassword } from '../lib/password.js'
5
6
 
6
7
  export class MyCustomerService extends AbstractService {
7
8
  public repository: CustomerRepository
@@ -51,7 +52,7 @@ export class MyCustomerService extends AbstractService {
51
52
 
52
53
  signIn(request: Request, response: Response) {
53
54
  const { email, password } = request.body
54
- const encodedPassword = Buffer.from(password).toString('base64')
55
+ const encodedPassword = hashPassword(password)
55
56
 
56
57
  const result = this.repository.query(getRepositoryContext(request), {
57
58
  where: [`email = "${email}"`, `password = "${encodedPassword}"`],