diff --git a/config.mjs b/config.mjs
index 693cffe5..a22e4983 100644
--- a/config.mjs
+++ b/config.mjs
@@ -44,7 +44,7 @@ export default {
expiresIn: '24h',
cookie_options: {
encoding: 'none', // we already used JWT to encode
- isSecure: true, // https only?
+ isSecure: false, // https only?
isHttpOnly: true, // prevent client alteration
clearInvalid: true, // remove invalid cookies
strictHeader: true, // don't allow violations of RFC 6265
diff --git a/lib/common/di/modules.mjs b/lib/common/di/modules.mjs
index 1fdd1d9a..1841c7dd 100644
--- a/lib/common/di/modules.mjs
+++ b/lib/common/di/modules.mjs
@@ -59,10 +59,9 @@ import UserService from '../../domain/services/UserService';
import sequelize from '../../infrastructure/database/sequelize';
import TransactionService from '../../domain/services/TransactionService';
import Sequelize from 'sequelize';
-import UserRemoteDataSource from '../../domain/repositories/UserRemoteDataSource';
-import UserRemoteDataSourceImpl from '../../interfaces/storage/UserRemoteDataSourceImpl';
-import CafeteriaRemoteDataSourceImpl from '../../interfaces/storage/CafeteriaRemoteDataSourceImpl';
-import CafeteriaRemoteDataSource from '../../domain/repositories/CafeteriaRemoteDataSource';
+import UserRemoteDataSource from '../../interfaces/storage/UserRemoteDataSource';
+import UserLocalDataSource from '../../interfaces/storage/UserLocalDataSource';
+import CafeteriaRemoteDataSource from '../../interfaces/storage/CafeteriaRemoteDataSource';
export default [
/**
@@ -180,6 +179,7 @@ export default [
{
create: async (r) => new UserRepositoryImpl({
db: await r(Sequelize),
+ localDataSource: await r(UserLocalDataSource),
remoteDataSource: await r(UserRemoteDataSource),
}),
as: UserRepository,
@@ -195,11 +195,15 @@ export default [
* Data Sources
*/
{
- create: async (r) => new CafeteriaRemoteDataSourceImpl(),
+ create: async (r) => new CafeteriaRemoteDataSource(),
as: CafeteriaRemoteDataSource,
},
{
- create: async (r) => new UserRemoteDataSourceImpl(),
+ create: async (r) => new UserLocalDataSource(),
+ as: UserLocalDataSource,
+ },
+ {
+ create: async (r) => new UserRemoteDataSource(),
as: UserRemoteDataSource,
},
diff --git a/lib/domain/repositories/CafeteriaRemoteDataSource.mjs b/lib/domain/repositories/CafeteriaDataSource.mjs
similarity index 91%
rename from lib/domain/repositories/CafeteriaRemoteDataSource.mjs
rename to lib/domain/repositories/CafeteriaDataSource.mjs
index 2a91ff70..eb9647b4 100644
--- a/lib/domain/repositories/CafeteriaRemoteDataSource.mjs
+++ b/lib/domain/repositories/CafeteriaDataSource.mjs
@@ -17,10 +17,10 @@
* along with this program. If not, see .
*/
-class CafeteriaRemoteDataSource {
+class CafeteriaDataSource {
fetchRawMenus() {
throw new Error('Not implemented!');
}
}
-export default CafeteriaRemoteDataSource;
+export default CafeteriaDataSource;
diff --git a/lib/domain/repositories/UserRemoteDataSource.mjs b/lib/domain/repositories/UserDataSource.mjs
similarity index 87%
rename from lib/domain/repositories/UserRemoteDataSource.mjs
rename to lib/domain/repositories/UserDataSource.mjs
index 6d687492..ae629513 100644
--- a/lib/domain/repositories/UserRemoteDataSource.mjs
+++ b/lib/domain/repositories/UserDataSource.mjs
@@ -17,10 +17,14 @@
* along with this program. If not, see .
*/
-class UserRemoteDataSource {
+class UserDataSource {
+ userExists(id) {
+ throw new Error('Not implemented!');
+ }
+
fetchLoginResult(id, password) {
throw new Error('Not implemented!');
}
}
-export default UserRemoteDataSource;
+export default UserDataSource;
diff --git a/lib/domain/repositories/UserRepository.mjs b/lib/domain/repositories/UserRepository.mjs
index 66a4858c..7fcd77ce 100644
--- a/lib/domain/repositories/UserRepository.mjs
+++ b/lib/domain/repositories/UserRepository.mjs
@@ -18,7 +18,7 @@
*/
class UserRepository {
- getRemoteLoginResult(id, password) {
+ getLoginResult(id, password) {
throw new Error('Not implemented!');
}
diff --git a/lib/domain/services/UserService.mjs b/lib/domain/services/UserService.mjs
index a390ae04..d1ab906b 100644
--- a/lib/domain/services/UserService.mjs
+++ b/lib/domain/services/UserService.mjs
@@ -53,7 +53,7 @@ class UserService {
}
async _loginWithIdAndPassword(id, password) {
- const remoteLoginResult = await this.userRepository.getRemoteLoginResult(id, password);
+ const remoteLoginResult = await this.userRepository.getLoginResult(id, password);
const isSucceeded = (remoteLoginResult === RemoteLoginResult.SUCCESS);
if (isSucceeded) {
diff --git a/lib/infrastructure/webserver/server.mjs b/lib/infrastructure/webserver/server.mjs
index 62691512..1a0c7dd4 100644
--- a/lib/infrastructure/webserver/server.mjs
+++ b/lib/infrastructure/webserver/server.mjs
@@ -65,7 +65,7 @@ async function registerPlugins(server) {
plugin: HapiSwagger,
options: {
host: config.server.host,
- schemes: ['https'],
+ schemes: ['http', 'https'],
info: {
title: '카페테리아 서버 API',
version: thisPackage.version,
diff --git a/lib/interfaces/controllers/UserController.mjs b/lib/interfaces/controllers/UserController.mjs
index 5da971c9..306d9577 100644
--- a/lib/interfaces/controllers/UserController.mjs
+++ b/lib/interfaces/controllers/UserController.mjs
@@ -77,14 +77,13 @@ class LoginHandler {
}
_successfulResponse(body, jwt) {
- return this._h
- .response(body)
- .state('token', jwt, config.auth.cookie_options) // cookie
- .header('Authorization', jwt); // header
+ return this._h.response(body)
+ .header('Authorization', jwt) // header
+ .state('token', jwt, config.auth.cookie_options); // cookie
}
_onLoginFailure(result) {
- logger.warn(`Failed attempt to login. Full request: ${JSON.stringify(this._request)}`);
+ logger.warn(`Failed attempt to login. Request payload: ${JSON.stringify(this._request.payload)}`);
switch (result) {
case LoginResults.WRONG_AUTH:
@@ -134,7 +133,7 @@ class LogoutHandler {
}
_successfulResponse() {
- return this._h
+ return this._h.response()
.code(204)
.state('token', 'expired', config.auth.cookie_options);
}
diff --git a/lib/interfaces/storage/CafeteriaRemoteDataSourceImpl.mjs b/lib/interfaces/storage/CafeteriaRemoteDataSource.mjs
similarity index 82%
rename from lib/interfaces/storage/CafeteriaRemoteDataSourceImpl.mjs
rename to lib/interfaces/storage/CafeteriaRemoteDataSource.mjs
index 1c9214f6..227c33d2 100644
--- a/lib/interfaces/storage/CafeteriaRemoteDataSourceImpl.mjs
+++ b/lib/interfaces/storage/CafeteriaRemoteDataSource.mjs
@@ -17,15 +17,15 @@
* along with this program. If not, see .
*/
-import CafeteriaRemoteDataSource from '../../domain/repositories/CafeteriaRemoteDataSource';
+import CafeteriaDataSource from '../../domain/repositories/CafeteriaDataSource';
import config from '../../../config';
import fetch from '../../common/utils/fetch';
-class CafeteriaRemoteDataSourceImpl extends CafeteriaRemoteDataSource {
+class CafeteriaRemoteDataSource extends CafeteriaDataSource {
async fetchRawMenus(url, date) {
return await fetch.getJson(config.menu.url, {stdDate: date});
}
}
-export default CafeteriaRemoteDataSourceImpl;
+export default CafeteriaRemoteDataSource;
diff --git a/lib/interfaces/storage/CafeteriaRepositoryImpl.mjs b/lib/interfaces/storage/CafeteriaRepositoryImpl.mjs
index 09980036..633a27f1 100644
--- a/lib/interfaces/storage/CafeteriaRepositoryImpl.mjs
+++ b/lib/interfaces/storage/CafeteriaRepositoryImpl.mjs
@@ -144,6 +144,17 @@ class CafeteriaRepositoryImpl extends CafeteriaRepository {
return [];
}
+ await this._fetchAndStoreIfNeeded(date);
+
+ if (!this._hasMenus(date)) {
+ logger.warn('No menu after fetch!');
+ return [];
+ }
+
+ return this.menuCache.menus[date];
+ }
+
+ async _fetchAndStoreIfNeeded(date) {
const nowMillis = Date.now();
const diff = nowMillis - this.menuCache.lastUpdatedMillis;
@@ -151,23 +162,22 @@ class CafeteriaRepositoryImpl extends CafeteriaRepository {
const noData = this.menuCache.menus[date] == null;
if (cacheOld || noData) {
- try {
- const json = await this.remoteDataSource.fetchRawMenus(config.menu.url, date);
- this.menuCache.menus[date] = this.menuConverter.convert(json);
- this.menuCache.lastUpdatedMillis = Date.now();
- } catch (e) {
- logger.error(e);
- }
+ await this._fetchAndStore(date);
}
+ }
- const hasData = Array.isArray(this.menuCache.menus[date]) && this.menuCache.menus[date].length;
-
- if (!hasData) {
- logger.warn('No menu after fetch!');
- return [];
+ async _fetchAndStore(date) {
+ try {
+ const json = await this.remoteDataSource.fetchRawMenus(config.menu.url, date);
+ this.menuCache.menus[date] = this.menuConverter.convert(json);
+ this.menuCache.lastUpdatedMillis = Date.now();
+ } catch (e) {
+ logger.error(e);
}
+ }
- return this.menuCache.menus[date];
+ _hasMenus(date) {
+ return Array.isArray(this.menuCache.menus[date]) && this.menuCache.menus[date].length;
}
}
diff --git a/lib/interfaces/storage/UserLocalDataSource.mjs b/lib/interfaces/storage/UserLocalDataSource.mjs
new file mode 100644
index 00000000..6db4212b
--- /dev/null
+++ b/lib/interfaces/storage/UserLocalDataSource.mjs
@@ -0,0 +1,38 @@
+/**
+ * This file is part of INU Cafeteria.
+ *
+ * Copyright (C) 2020 INU Global App Center
+ *
+ * INU Cafeteria is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * INU Cafeteria is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+import UserDataSource from '../../domain/repositories/UserDataSource';
+import getEnv from '../../common/utils/env';
+import RemoteLoginResult from '../../domain/constants/RemoteLoginResult';
+
+class UserLocalDataSource extends UserDataSource {
+ userExists(id) {
+ return String(id) === getEnv('TEST_ID');
+ }
+
+ async fetchLoginResult(id, password) {
+ if (String(id) === getEnv('TEST_ID') && password === getEnv('TEST_PW')) {
+ return RemoteLoginResult.SUCCESS;
+ } else {
+ return RemoteLoginResult.FAIL;
+ }
+ }
+}
+
+export default UserLocalDataSource;
diff --git a/lib/interfaces/storage/UserRemoteDataSourceImpl.mjs b/lib/interfaces/storage/UserRemoteDataSource.mjs
similarity index 83%
rename from lib/interfaces/storage/UserRemoteDataSourceImpl.mjs
rename to lib/interfaces/storage/UserRemoteDataSource.mjs
index b1a8457e..b1a20c2c 100644
--- a/lib/interfaces/storage/UserRemoteDataSourceImpl.mjs
+++ b/lib/interfaces/storage/UserRemoteDataSource.mjs
@@ -17,14 +17,19 @@
* along with this program. If not, see .
*/
-import UserRemoteDataSource from '../../domain/repositories/UserRemoteDataSource';
import config from '../../../config';
import fetch from '../../common/utils/fetch';
import encrypt from '../../common/utils/encrypt';
import logger from '../../common/utils/logger';
import RemoteLoginResult from '../../domain/constants/RemoteLoginResult';
+import UserDataSource from '../../domain/repositories/UserDataSource';
+import getEnv from '../../common/utils/env';
+
+class UserRemoteDataSource extends UserDataSource {
+ async userExists(id) {
+ return id !== getEnv('TEST_ID');
+ }
-class UserRemoteDataSourceImpl extends UserRemoteDataSource {
async fetchLoginResult(id, password) {
try {
return await fetch.postAndGetReponseText(
@@ -42,4 +47,4 @@ class UserRemoteDataSourceImpl extends UserRemoteDataSource {
}
}
-export default UserRemoteDataSourceImpl;
+export default UserRemoteDataSource;
diff --git a/lib/interfaces/storage/UserRepositoryImpl.mjs b/lib/interfaces/storage/UserRepositoryImpl.mjs
index bbf44469..76d66349 100644
--- a/lib/interfaces/storage/UserRepositoryImpl.mjs
+++ b/lib/interfaces/storage/UserRepositoryImpl.mjs
@@ -29,21 +29,25 @@ const {Sequelize} = seq;
* Implementation of UserRepository.
*/
class UserRepositoryImpl extends UserRepository {
- constructor({db, remoteDataSource}) {
+ constructor({db, localDataSource, remoteDataSource}) {
super();
this.db = db;
this.userModel = this.db.model('user');
- this.remoteDataSource = remoteDataSource;
+ this.userDataSources = [localDataSource, remoteDataSource];
}
- async getRemoteLoginResult(id, password) {
+ async getLoginResult(id, password) {
if (!id || !password) {
return RemoteLoginResult.FUCK;
}
- return this.remoteDataSource.fetchLoginResult(id, password);
+ for (const dataSource of this.userDataSources) {
+ if (dataSource.userExists(id)) {
+ return dataSource.fetchLoginResult(id, password);
+ }
+ }
}
async findUserById(id) {
diff --git a/test/mocks/UserRepositoryMock.mjs b/test/mocks/UserRepositoryMock.mjs
index d73199dd..f17502f0 100644
--- a/test/mocks/UserRepositoryMock.mjs
+++ b/test/mocks/UserRepositoryMock.mjs
@@ -28,7 +28,7 @@ class UserRepositoryMock extends UserRepository {
super();
}
- getRemoteLoginResult(id, password) {
+ getLoginResult(id, password) {
throw new Error('Not mocked! You need extra mock here');
}
diff --git a/test/unit/domain/services/UserService.test.mjs b/test/unit/domain/services/UserService.test.mjs
index 12de8221..9602b215 100644
--- a/test/unit/domain/services/UserService.test.mjs
+++ b/test/unit/domain/services/UserService.test.mjs
@@ -128,7 +128,7 @@ describe('# Logout', () => {
const setRemoteLoginResultMock = function(result) {
const mock = jest.fn(() => result);
- resolve(UserService).userRepository.getRemoteLoginResult = mock;
+ resolve(UserService).userRepository.getLoginResult = mock;
return mock;
};
diff --git a/test/unit/interfaces/storage/CafeteriaRepositoryImpl.test.mjs b/test/unit/interfaces/storage/CafeteriaRepositoryImpl.test.mjs
index a674dbb5..01df7e4d 100644
--- a/test/unit/interfaces/storage/CafeteriaRepositoryImpl.test.mjs
+++ b/test/unit/interfaces/storage/CafeteriaRepositoryImpl.test.mjs
@@ -21,7 +21,7 @@ import CafeteriaRepositoryImpl from '../../../../lib/interfaces/storage/Cafeteri
import MenuConverter from '../../../../lib/interfaces/converters/MenuConverter';
import config from '../../../../config';
import sequelize from '../../infrastructure/database/sequelizeMock';
-import CafeteriaRemoteDataSource from '../../../../lib/domain/repositories/CafeteriaRemoteDataSource';
+import CafeteriaDataSource from '../../../../lib/domain/repositories/CafeteriaDataSource';
describe('# getAllCafeteria', () => {
it('should succeed', async () => {
@@ -140,7 +140,7 @@ describe('# getMenusByCornerId', () => {
const getRepository = function() {
return new CafeteriaRepositoryImpl({
db: sequelize,
- remoteDataSource: new (class CafeteriaRemoteDataSourceMock extends CafeteriaRemoteDataSource {
+ remoteDataSource: new (class CafeteriaRemoteDataSourceMock extends CafeteriaDataSource {
fetchRawMenus() {
return rawMenuExample;
}
diff --git a/test/unit/interfaces/storage/UserRepositoryImpl.test.mjs b/test/unit/interfaces/storage/UserRepositoryImpl.test.mjs
index 4c0d922a..ac46761a 100644
--- a/test/unit/interfaces/storage/UserRepositoryImpl.test.mjs
+++ b/test/unit/interfaces/storage/UserRepositoryImpl.test.mjs
@@ -19,27 +19,27 @@
import UserRepositoryImpl from '../../../../lib/interfaces/storage/UserRepositoryImpl';
import sequelize from '../../infrastructure/database/sequelizeMock';
-import UserRemoteDataSource from '../../../../lib/domain/repositories/UserRemoteDataSource';
import RemoteLoginResult from '../../../../lib/domain/constants/RemoteLoginResult';
+import UserDataSource from '../../../../lib/domain/repositories/UserDataSource';
describe('# getRemoteLoginResult', () => {
it('should catch null id or password', async () => {
const repo = getRepository();
- const result = await repo.getRemoteLoginResult(null, null);
+ const result = await repo.getLoginResult(null, null);
expect(result).toBe(RemoteLoginResult.FUCK);
});
it('should fail with wrong auth', async () => {
const repo = getRepository();
- const result = await repo.getRemoteLoginResult(3543534242, 'hehe');
+ const result = await repo.getLoginResult(3543534242, 'hehe');
expect(result).toBe(RemoteLoginResult.FAIL);
});
it('should succeed', async () => {
const repo = getRepository();
- const result = await repo.getRemoteLoginResult(201701562, 'password');
+ const result = await repo.getLoginResult(201701562, 'password');
expect(result).toBe(RemoteLoginResult.SUCCESS);
});
@@ -70,7 +70,7 @@ describe('# addOrUpdateUser', () => {
const upsertMock = jest.fn();
sequelize.model('user').upsert = upsertMock;
- repo.addOrUpdateUser(null, {});
+ await repo.addOrUpdateUser(null, {});
expect(upsertMock).toBeCalledTimes(0);
});
@@ -81,7 +81,7 @@ describe('# addOrUpdateUser', () => {
const upsertMock = jest.fn();
sequelize.model('user').upsert = upsertMock;
- repo.addOrUpdateUser(201701562, {token: 'token', barcode: 'barcode'});
+ await repo.addOrUpdateUser(201701562, {token: 'token', barcode: 'barcode'});
expect(upsertMock).toBeCalledTimes(1);
expect(upsertMock).toBeCalledWith({id: 201701562, token: 'token', barcode: 'barcode'});
@@ -95,7 +95,7 @@ describe('# updateLastLoginTimestamp', () => {
const upsertMock = jest.fn();
sequelize.model('user').upsert = upsertMock;
- repo.updateLastLoginTimestamp(201701562);
+ await repo.updateLastLoginTimestamp(201701562);
expect(upsertMock).toBeCalledTimes(1);
});
@@ -108,16 +108,18 @@ describe('# updateLastLogoutTimestamp', () => {
const upsertMock = jest.fn();
sequelize.model('user').upsert = upsertMock;
- repo.updateLastLogoutTimestamp(201701562);
+ await repo.updateLastLogoutTimestamp(201701562);
expect(upsertMock).toBeCalledTimes(1);
});
});
const getRepository = function() {
- return new UserRepositoryImpl({
- db: sequelize,
- remoteDataSource: new (class UserRemoteDataSourceMock extends UserRemoteDataSource {
+ const dataSource = new (class UserRemoteDataSourceMock extends UserDataSource {
+ userExists(id) {
+ return true;
+ }
+
fetchLoginResult(id, password) {
if (id === 201701562 && password === 'password') {
return RemoteLoginResult.SUCCESS;
@@ -125,6 +127,11 @@ const getRepository = function() {
return RemoteLoginResult.FAIL;
}
}
- }),
+ });
+
+ return new UserRepositoryImpl({
+ db: sequelize,
+ localDataSource: dataSource,
+ remoteDataSource: dataSource,
});
};