├── web-ui ├── src │ ├── lang │ │ ├── i18n.ts │ │ ├── jp.ts │ │ ├── en.ts │ │ └── hi.ts │ ├── components │ │ ├── PageContainer.vue │ │ ├── Logo.vue │ │ ├── Legend.vue │ │ ├── AppHeader.vue │ │ └── AppShell.vue │ ├── assets │ │ ├── styles │ │ │ ├── _vars.scss │ │ │ └── _fonts.scss │ │ └── fonts │ │ │ ├── ClarityCity-Bold.woff2 │ │ │ ├── ClarityCity-Light.woff2 │ │ │ ├── ClarityCity-Medium.woff2 │ │ │ ├── ClarityCity-Regular.woff2 │ │ │ ├── ClarityCity-SemiBold.woff2 │ │ │ └── ClarityCity-RegularItalic.woff2 │ ├── views │ │ ├── About.vue │ │ ├── Home.vue │ │ ├── Register.vue │ │ ├── EmployeeDetails.vue │ │ ├── CustomerDetails.vue │ │ ├── ProductDetails.vue │ │ ├── Products.vue │ │ └── LoginPage.vue │ ├── App.vue │ ├── shims-vue.d.ts │ ├── api │ │ ├── cart-api.ts │ │ ├── stats-api.ts │ │ ├── product-api.ts │ │ ├── customer-api.ts │ │ ├── employee-api.ts │ │ ├── users-api.ts │ │ ├── order-api.ts │ │ └── api-service.ts │ ├── menu │ │ └── TopNav.ts │ ├── shared │ │ ├── chart-options.ts │ │ └── utils.ts │ ├── router │ │ └── index.ts │ ├── main.ts │ └── store │ │ └── index.ts ├── .browserslistrc ├── babel.config.js ├── public │ ├── favicon.png │ └── index.html ├── .editorconfig ├── .gitignore ├── .eslintrc.js ├── vue.config.js ├── tsconfig.json ├── dist │ └── index.html └── package.json ├── system.properties ├── Procfile ├── screenshots ├── login.png ├── dashboard.png └── api_reference.png ├── web-api └── src │ └── main │ ├── webapp │ ├── favicon.png │ ├── api-docs │ │ ├── favicon.png │ │ └── index.html │ ├── ui │ │ ├── img │ │ │ └── color.c7a33805.png │ │ └── fonts │ │ │ ├── primeicons.121254f7.ttf │ │ │ ├── primeicons.b0f5d02f.eot │ │ │ ├── primeicons.3b6e3706.woff │ │ │ ├── ClarityCity-Light.4fea0601.woff2 │ │ │ ├── ClarityCity-Medium.430ba5ba.woff2 │ │ │ ├── ClarityCity-Regular.b9f19aae.woff2 │ │ │ └── ClarityCity-SemiBold.e8b53eee.woff2 │ ├── index.html │ └── WEB-INF │ │ └── web.xml │ ├── resources │ ├── log4j2.properties │ └── hibernate.cfg.bak │ └── java │ ├── com │ └── app │ │ ├── util │ │ ├── Constants.java │ │ ├── TokenUtil.java │ │ └── HibernateUtil.java │ │ ├── model │ │ ├── order │ │ │ ├── OrderWithNestedDetailResponse.java │ │ │ ├── OrderItemModel.java │ │ │ ├── OrderInfoModel.java │ │ │ ├── OrderModel.java │ │ │ └── OrderWithNestedDetailModel.java │ │ ├── stats │ │ │ ├── CategoryCountModel.java │ │ │ ├── DailyOrderCountModel.java │ │ │ └── DailySaleModel.java │ │ ├── user │ │ │ ├── LoginModel.java │ │ │ ├── UserModel.java │ │ │ ├── UserViewModel.java │ │ │ ├── UserOutputModel.java │ │ │ └── UserRegistrationModel.java │ │ ├── BaseResponse.java │ │ ├── PageResponse.java │ │ ├── cart │ │ │ ├── CartModel.java │ │ │ └── CartViewModel.java │ │ ├── product │ │ │ └── ProductModel.java │ │ ├── customer │ │ │ ├── CustomerModel.java │ │ │ └── CustomerUserModel.java │ │ └── employee │ │ │ ├── EmployeeModel.java │ │ │ └── EmployeeUserModel.java │ │ ├── api │ │ ├── BaseController.java │ │ ├── HandleInputJsonParseException.java │ │ ├── RelayRuntimeExceptionInResponse.java │ │ ├── controllers │ │ │ ├── AuthenticationController.java │ │ │ └── StatsController.java │ │ └── MiscController.java │ │ ├── WebContextListener.txt │ │ ├── task │ │ └── RefreshDBTask.java │ │ ├── filters │ │ ├── CORSResponseFilter.java │ │ └── AuthorizationRequestFilter.java │ │ ├── dao │ │ ├── EmployeeDao.java │ │ ├── ProductDao.java │ │ ├── CustomerDao.java │ │ ├── CartDao.java │ │ ├── UserDao.java │ │ └── StatsDao.java │ │ ├── JerseyApplication.java │ │ └── TomcatStarter.java │ └── servlet │ └── HelloServlet.java ├── database ├── src │ └── main │ │ └── java │ │ ├── module-info.java │ │ └── com │ │ └── app │ │ └── DatabaseService.java └── pom.xml ├── .gitignore ├── pom.xml └── README.md /web-ui/src/lang/i18n.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /system.properties: -------------------------------------------------------------------------------- 1 | java.runtime.version=15 -------------------------------------------------------------------------------- /web-ui/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java $JAVA_OPTS -Dserver.port=$PORT -jar web-api/target/modules/web-api-1.0.0.jar -------------------------------------------------------------------------------- /screenshots/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/screenshots/login.png -------------------------------------------------------------------------------- /web-ui/src/components/PageContainer.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /screenshots/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/screenshots/dashboard.png -------------------------------------------------------------------------------- /web-ui/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset', 4 | ], 5 | }; 6 | -------------------------------------------------------------------------------- /web-ui/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-ui/public/favicon.png -------------------------------------------------------------------------------- /screenshots/api_reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/screenshots/api_reference.png -------------------------------------------------------------------------------- /web-ui/src/assets/styles/_vars.scss: -------------------------------------------------------------------------------- 1 | $font_path: '~@/assets/fonts/'; 2 | $regular-font: 'clarity'; 3 | $primary-color: #ff791A; -------------------------------------------------------------------------------- /web-api/src/main/webapp/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-api/src/main/webapp/favicon.png -------------------------------------------------------------------------------- /web-ui/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /web-api/src/main/webapp/api-docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-api/src/main/webapp/api-docs/favicon.png -------------------------------------------------------------------------------- /web-ui/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /web-ui/src/assets/fonts/ClarityCity-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-ui/src/assets/fonts/ClarityCity-Bold.woff2 -------------------------------------------------------------------------------- /web-api/src/main/webapp/ui/img/color.c7a33805.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-api/src/main/webapp/ui/img/color.c7a33805.png -------------------------------------------------------------------------------- /web-ui/src/assets/fonts/ClarityCity-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-ui/src/assets/fonts/ClarityCity-Light.woff2 -------------------------------------------------------------------------------- /web-ui/src/assets/fonts/ClarityCity-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-ui/src/assets/fonts/ClarityCity-Medium.woff2 -------------------------------------------------------------------------------- /web-ui/src/assets/fonts/ClarityCity-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-ui/src/assets/fonts/ClarityCity-Regular.woff2 -------------------------------------------------------------------------------- /web-ui/src/assets/fonts/ClarityCity-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-ui/src/assets/fonts/ClarityCity-SemiBold.woff2 -------------------------------------------------------------------------------- /database/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module com.app { 2 | requires org.slf4j; 3 | requires java.sql; 4 | requires com.h2database; 5 | exports com.app; 6 | } -------------------------------------------------------------------------------- /web-api/src/main/webapp/ui/fonts/primeicons.121254f7.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-api/src/main/webapp/ui/fonts/primeicons.121254f7.ttf -------------------------------------------------------------------------------- /web-api/src/main/webapp/ui/fonts/primeicons.b0f5d02f.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-api/src/main/webapp/ui/fonts/primeicons.b0f5d02f.eot -------------------------------------------------------------------------------- /web-ui/src/assets/fonts/ClarityCity-RegularItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-ui/src/assets/fonts/ClarityCity-RegularItalic.woff2 -------------------------------------------------------------------------------- /web-api/src/main/webapp/ui/fonts/primeicons.3b6e3706.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-api/src/main/webapp/ui/fonts/primeicons.3b6e3706.woff -------------------------------------------------------------------------------- /web-api/src/main/webapp/ui/fonts/ClarityCity-Light.4fea0601.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-api/src/main/webapp/ui/fonts/ClarityCity-Light.4fea0601.woff2 -------------------------------------------------------------------------------- /web-api/src/main/webapp/ui/fonts/ClarityCity-Medium.430ba5ba.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-api/src/main/webapp/ui/fonts/ClarityCity-Medium.430ba5ba.woff2 -------------------------------------------------------------------------------- /web-api/src/main/webapp/ui/fonts/ClarityCity-Regular.b9f19aae.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-api/src/main/webapp/ui/fonts/ClarityCity-Regular.b9f19aae.woff2 -------------------------------------------------------------------------------- /web-api/src/main/webapp/ui/fonts/ClarityCity-SemiBold.e8b53eee.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrin9/Modular-Java-Jersey-Vue/HEAD/web-api/src/main/webapp/ui/fonts/ClarityCity-SemiBold.e8b53eee.woff2 -------------------------------------------------------------------------------- /web-ui/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | end_of_line = lf 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | max_line_length = 100 8 | -------------------------------------------------------------------------------- /web-ui/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /web-api/src/main/resources/log4j2.properties: -------------------------------------------------------------------------------- 1 | appender.console.type = Console 2 | appender.console.name = console 3 | appender.console.layout.type = PatternLayout 4 | 5 | rootLogger.level = info 6 | rootLogger.appenderRef.console.ref = console -------------------------------------------------------------------------------- /web-ui/src/lang/jp.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | root: { 3 | product_name: '電子メールセキュリティ', 4 | change_lang: '言語を変えてください', 5 | login: 'ログイン', 6 | username: 'ユーザー名', 7 | password: 'パスワード', 8 | domain: 'ドメイン', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /web-ui/src/lang/en.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | root: { 3 | product_name: 'MRIN', 4 | change_lang: 'Change Language', 5 | login: 'LOGIN', 6 | username: 'User Name', 7 | password: 'Password', 8 | domain: 'Domain', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /web-ui/src/lang/hi.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | root: { 3 | product_name: 'मृन', 4 | change_lang: 'भाषा बदलो', 5 | login: 'लॉग इन करें', 6 | username: 'उपयोगकर्ता नाम', 7 | password: 'पारण शब्द', 8 | domain: 'डोमेन', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | **/target 3 | **/.classpath 4 | **/.project 5 | **/.settings 6 | **/.idea/ 7 | **/nbactions.xml 8 | **/nb-configuration.xml 9 | apache-tomcat* 10 | **/apache-tomcat*/ 11 | **/.vscode 12 | **/.babelrc 13 | **/.postcssrc 14 | **/*.iml 15 | **/node_modules/ 16 | **/web-ui/node -------------------------------------------------------------------------------- /web-ui/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/util/Constants.java: -------------------------------------------------------------------------------- 1 | package com.app.util; 2 | 3 | public class Constants { 4 | public static class UserRoleConstants { 5 | public final static String ROLE_ADMIN = "ADMIN"; 6 | public final static String ROLE_CUSTOMER = "CUSTOMER"; 7 | public final static String ROLE_SUPPORT = "SUPPORT"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /web-ui/src/api/cart-api.ts: -------------------------------------------------------------------------------- 1 | import api from '@/api/api-service'; 2 | import { AxiosResponse } from 'axios'; 3 | 4 | export default { 5 | async getCartItems(userId: string): Promise { 6 | const qsParams: Record = { 7 | 'user-id': userId || '', 8 | }; 9 | return api.get('/cart', { params: qsParams }); 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/order/OrderWithNestedDetailResponse.java: -------------------------------------------------------------------------------- 1 | package com.app.model.order; 2 | 3 | import com.app.model.PageResponse; 4 | import java.util.*; 5 | 6 | public class OrderWithNestedDetailResponse extends PageResponse { 7 | private List list = new ArrayList<>(); 8 | 9 | public List getList() { return list; } 10 | public void setList(List list) { this.list = list; } 11 | } 12 | -------------------------------------------------------------------------------- /web-ui/src/api/stats-api.ts: -------------------------------------------------------------------------------- 1 | import api from '@/api/api-service'; 2 | import { AxiosResponse } from 'axios'; 3 | 4 | export default { 5 | async getDailySale(): Promise { 6 | return api.get('/stats/daily-sale'); 7 | }, 8 | 9 | async getDailyOrderCount(): Promise { 10 | return api.get('/stats/daily-order-count'); 11 | }, 12 | 13 | async getOrdersStats(groupBy: 'by-status' | 'by-payment-type'): Promise { 14 | if (groupBy === 'by-status') { 15 | return api.get('/stats/orders-by-status'); 16 | } 17 | return api.get('/stats/orders-by-payment-type'); 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/api/BaseController.java: -------------------------------------------------------------------------------- 1 | package com.app.api; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import jakarta.servlet.http.HttpServletResponse; 5 | import jakarta.ws.rs.container.ContainerRequestContext; 6 | import jakarta.ws.rs.core.Context; 7 | import jakarta.ws.rs.core.SecurityContext; 8 | public class BaseController { 9 | @Context 10 | protected HttpServletRequest req; 11 | 12 | @Context 13 | protected HttpServletResponse res; 14 | 15 | @Context 16 | protected ContainerRequestContext requestContext; 17 | 18 | @Context 19 | protected SecurityContext securityContext; 20 | } 21 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/WebContextListener.txt: -------------------------------------------------------------------------------- 1 | package com.app; 2 | 3 | import jakarta.servlet.ServletContextEvent; 4 | import jakarta.servlet.ServletContextListener; 5 | import jakarta.servlet.annotation.WebListener; 6 | 7 | 8 | @WebListener 9 | public class WebContextListener implements ServletContextListener { 10 | 11 | @Override 12 | public void contextInitialized(ServletContextEvent servletContextEvent) { 13 | System.out.println("Starting up WEB UI!"); 14 | 15 | } 16 | 17 | @Override 18 | public void contextDestroyed(ServletContextEvent servletContextEvent) { 19 | System.out.println("Shutting down WEB UI!"); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /web-ui/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 24 | -------------------------------------------------------------------------------- /web-ui/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | MRIN Order Processing Sys 8 | 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/task/RefreshDBTask.java: -------------------------------------------------------------------------------- 1 | package com.app.task; 2 | 3 | import com.app.DatabaseService; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | public class RefreshDBTask implements Runnable { 8 | private static final Logger log = LoggerFactory.getLogger(RefreshDBTask.class); 9 | @Override 10 | public void run() { 11 | 12 | //System.out.print("\n\n" + Thread.currentThread().getName()+" START: "+new Date()); 13 | DatabaseService.initDB(); 14 | //System.out.print(" ...... " + Thread.currentThread().getName()+" DONE: "+new Date()); 15 | if (Thread.interrupted()){ 16 | log.warn("Refresh DB Task Interrupted"); 17 | return; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /web-ui/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | 'plugin:vue/vue3-essential', 8 | '@vue/airbnb', 9 | '@vue/typescript/recommended', 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 2020, 13 | }, 14 | rules: { 15 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 17 | 'max-len': [2, 200, 2], 18 | 'object-curly-newline': [ 19 | 'warn', { 20 | ObjectExpression: { multiline: true, minProperties: 5, consistent: true }, 21 | ObjectPattern: { multiline: true, minProperties: 5, consistent: true }, 22 | ImportDeclaration: 'never', 23 | ExportDeclaration: 'never', 24 | }, 25 | ], 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /web-api/src/main/java/servlet/HelloServlet.java: -------------------------------------------------------------------------------- 1 | package servlet; 2 | 3 | import java.io.IOException; 4 | 5 | import jakarta.servlet.ServletException; 6 | import jakarta.servlet.ServletOutputStream; 7 | import jakarta.servlet.annotation.WebServlet; 8 | import jakarta.servlet.http.HttpServlet; 9 | import jakarta.servlet.http.HttpServletRequest; 10 | import jakarta.servlet.http.HttpServletResponse; 11 | 12 | @WebServlet(name = "HelloWorldServlet", urlPatterns = {"/hello-world"}) 13 | public class HelloServlet extends HttpServlet { 14 | 15 | @Override 16 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 17 | ServletOutputStream out = resp.getOutputStream(); 18 | out.write("hello world !!!".getBytes()); 19 | out.flush(); 20 | out.close(); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /web-ui/vue.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | assetsDir: './ui', 6 | devServer: { 7 | port: 8081, 8 | }, 9 | pages: { 10 | index: { 11 | // entry for the page 12 | entry: 'src/main.ts', 13 | // the source template 14 | template: 'public/index.html', 15 | // output as dist/index.html 16 | filename: 'index.html', 17 | // when using title option, 18 | // template title tag needs to be <%= htmlWebpackPlugin.options.title %> 19 | title: 'Mrin - Order Processing System', 20 | // chunks to include on this page, by default includes 21 | // extracted common chunks and vendor chunks. 22 | chunks: ['chunk-vendors', 'chunk-common', 'index'], 23 | }, 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /web-ui/src/assets/styles/_fonts.scss: -------------------------------------------------------------------------------- 1 | @import "~@/assets/styles/_vars.scss"; 2 | 3 | //Light 4 | @font-face { 5 | font-family: 'clarity'; 6 | src: url($font_path + 'ClarityCity-Light.woff2')format('woff2') ; 7 | font-weight: 200; 8 | font-style: normal; 9 | } 10 | 11 | //Regular 12 | @font-face { 13 | font-family: 'clarity'; 14 | src: url($font_path + 'ClarityCity-Regular.woff2')format('woff2') ; 15 | font-weight: 400; 16 | font-style: normal; 17 | } 18 | 19 | //Semibold 20 | @font-face { 21 | font-family: 'clarity'; 22 | src: url($font_path + 'ClarityCity-Medium.woff2')format('woff2') ; 23 | font-weight: 500; 24 | font-style: normal; 25 | } 26 | 27 | //Bold 28 | @font-face { 29 | font-family: 'clarity'; 30 | src: url($font_path + 'ClarityCity-SemiBold.woff2')format('woff2') ; 31 | font-weight: 600; 32 | font-style: normal; 33 | } 34 | -------------------------------------------------------------------------------- /web-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "types": [ 15 | "webpack-env" 16 | ], 17 | "paths": { 18 | "@/*": [ 19 | "src/*" 20 | ] 21 | }, 22 | "lib": [ 23 | "esnext", 24 | "dom", 25 | "dom.iterable", 26 | "scripthost" 27 | ] 28 | }, 29 | "include": [ 30 | "src/**/*.ts", 31 | "src/**/*.tsx", 32 | "src/**/*.vue", 33 | "tests/**/*.ts", 34 | "tests/**/*.tsx" 35 | ], 36 | "exclude": [ 37 | "node_modules" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/api/HandleInputJsonParseException.java: -------------------------------------------------------------------------------- 1 | package com.app.api; 2 | 3 | import com.app.model.BaseResponse; 4 | import org.glassfish.jersey.server.ParamException; 5 | import jakarta.ws.rs.core.MediaType; 6 | import jakarta.ws.rs.core.Response; 7 | import jakarta.ws.rs.ext.ExceptionMapper; 8 | import jakarta.ws.rs.ext.Provider; 9 | 10 | @Provider 11 | public class HandleInputJsonParseException implements ExceptionMapper{ 12 | @Override 13 | public Response toResponse(ParamException e) { 14 | BaseResponse resp = new BaseResponse(); 15 | resp.setErrorMessage("illegal value for parameter '" + e.getParameterName() + "'"); 16 | return Response 17 | .status(Response.Status.BAD_REQUEST) 18 | .entity(resp) 19 | .type(MediaType.APPLICATION_JSON) 20 | .build(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /web-ui/dist/index.html: -------------------------------------------------------------------------------- 1 | MRIN Order Processing Sys
-------------------------------------------------------------------------------- /web-api/src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | MRIN Order Processing Sys
-------------------------------------------------------------------------------- /web-ui/src/api/product-api.ts: -------------------------------------------------------------------------------- 1 | import api from '@/api/api-service'; 2 | import { AxiosResponse } from 'axios'; 3 | 4 | export default { 5 | async getProducts(page = 1, pageSize = 20, productId = '', category = ''): Promise { 6 | const qsParams: Record = { }; 7 | if (page) { qsParams.page = page; } 8 | if (pageSize) { qsParams['page-size'] = pageSize; } 9 | if (productId) { qsParams.id = productId; } 10 | if (category) { qsParams.category = category; } 11 | 12 | return api.get('/products', { params: qsParams }); 13 | }, 14 | 15 | async deleteProduct(productId: string): Promise { 16 | return api.delete(`/products/${productId}`); 17 | }, 18 | 19 | async addProduct(productObj: Record): Promise { 20 | return api.post('/products', productObj); 21 | }, 22 | 23 | async updateProduct(productObj: Record): Promise { 24 | return api.put('/products', productObj); 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /web-api/src/main/resources/hibernate.cfg.bak: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | org.h2.Driver 7 | jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 8 | sa 9 | sa 10 | NORTHWIND 11 | org.hibernate.dialect.H2Dialect 12 | true 13 | true 14 | update 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /web-ui/src/api/customer-api.ts: -------------------------------------------------------------------------------- 1 | import api from '@/api/api-service'; 2 | import { AxiosResponse } from 'axios'; 3 | 4 | export default { 5 | async getCustomers(page = 1, pageSize = 20, customerId = '', name = '', company = ''): Promise { 6 | const qsParams: Record = { }; 7 | if (customerId) { qsParams.id = customerId; } 8 | if (page) { qsParams.page = page; } 9 | if (pageSize) { qsParams['page-size'] = pageSize; } 10 | if (name) { qsParams.name = `%${name}%`; } 11 | if (company) { qsParams.company = company; } 12 | 13 | return api.get('/customers', { params: qsParams }); 14 | }, 15 | 16 | async deleteCustomer(customerId: string): Promise { 17 | return api.delete(`/customers/${customerId}`); 18 | }, 19 | 20 | async addCustomer(customerObj: Record): Promise { 21 | return api.post('/customers', customerObj); 22 | }, 23 | 24 | async updateCustomer(customerObj: Record): Promise { 25 | return api.put('/customers', customerObj); 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/stats/CategoryCountModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.stats; 2 | 3 | import com.app.model.BaseResponse; 4 | import java.math.BigInteger; 5 | import java.util.List; 6 | 7 | public class CategoryCountModel { 8 | private String category; 9 | private BigInteger count; 10 | 11 | //Constructor 12 | public CategoryCountModel(String category, BigInteger count) { 13 | this.category = category; 14 | this.count = count; 15 | } 16 | 17 | //Getters and Setters 18 | public String getCategory() { return category; } 19 | public void setCategory(String category) { this.category = category; } 20 | 21 | public BigInteger getCount() { return count; } 22 | public void setCount(BigInteger count) { this.count = count; } 23 | 24 | //Response Model Class used as API response 25 | public static class CategoryCountResponse extends BaseResponse { 26 | private List list; 27 | 28 | public List getList() {return list; } 29 | public void setList(List list) { this.list = list; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /web-ui/src/api/employee-api.ts: -------------------------------------------------------------------------------- 1 | import api from '@/api/api-service'; 2 | import { AxiosResponse } from 'axios'; 3 | 4 | export default { 5 | async getEmployees(page = 1, pageSize = 20, employeeId = '', nameOrEmail = '', department = ''): Promise { 6 | const qsParams: Record = { }; 7 | if (employeeId) { qsParams.id = employeeId; } 8 | if (page) { qsParams.page = page; } 9 | if (pageSize) { qsParams['page-size'] = pageSize; } 10 | if (nameOrEmail) { qsParams.search = `%${nameOrEmail}%`; } 11 | if (department) { qsParams.department = department; } 12 | 13 | return api.get('/employees', { params: qsParams }); 14 | }, 15 | 16 | async deleteEmployee(employeeId: string): Promise { 17 | return api.delete(`/employees/${employeeId}`); 18 | }, 19 | 20 | async addEmployee(employeeObj: Record): Promise { 21 | return api.post('/employees', employeeObj); 22 | }, 23 | 24 | async updateEmployee(employeeObj: Record): Promise { 25 | return api.put('/employees', employeeObj); 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/stats/DailyOrderCountModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.stats; 2 | 3 | import com.app.model.BaseResponse; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import java.math.BigInteger; 6 | import java.util.*; 7 | 8 | public class DailyOrderCountModel { 9 | @JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss") 10 | private Date date; 11 | private BigInteger count; 12 | 13 | //Constructor 14 | public DailyOrderCountModel(Date date, BigInteger count) { 15 | this.date = date; 16 | this.count = count; 17 | } 18 | 19 | //Getters and Setters 20 | public Date getDate() { return date; } 21 | public void setDate(Date date) { this.date = date; } 22 | 23 | public BigInteger getCount() { return count; } 24 | public void setCount(BigInteger count) { this.count = count; } 25 | 26 | //Response Class 27 | public static class DailyOrderCountResponse extends BaseResponse { 28 | private List list; 29 | 30 | public List getList() {return list; } 31 | public void setList(List list) { this.list = list; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/filters/CORSResponseFilter.java: -------------------------------------------------------------------------------- 1 | package com.app.filters; 2 | 3 | import java.io.IOException; 4 | import jakarta.annotation.Priority; 5 | import jakarta.ws.rs.container.*; 6 | import jakarta.ws.rs.core.MultivaluedMap; 7 | 8 | @Priority(0) 9 | public class CORSResponseFilter implements ContainerResponseFilter { 10 | 11 | @Override 12 | public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { 13 | MultivaluedMap respHeaderMap = responseContext.getHeaders(); 14 | String reqHeaderString = requestContext.getHeaderString("Access-Control-Request-Headers"); 15 | 16 | respHeaderMap.add("Access-Control-Allow-Origin", "*"); 17 | respHeaderMap.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); 18 | respHeaderMap.add("Access-Control-Allow-Credentials", "true"); 19 | respHeaderMap.add("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, Referer, Connection, User-Agent, authorization, timezone-id"); 20 | //respHeaderMap.add("Access-Control-Allow-Headers", reqHeaderString); 21 | respHeaderMap.add("X-Powered-By", "Mrin-Order-API"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /web-ui/src/components/Logo.vue: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 22 | -------------------------------------------------------------------------------- /web-ui/src/views/Register.vue: -------------------------------------------------------------------------------- 1 | 12 | 24 | 45 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/dao/EmployeeDao.java: -------------------------------------------------------------------------------- 1 | package com.app.dao; 2 | 3 | import com.app.model.employee.EmployeeModel; 4 | import org.hibernate.*; 5 | import jakarta.validation.ConstraintViolationException; 6 | 7 | public class EmployeeDao { 8 | public static EmployeeModel getById(Session hbrSession, Integer employeeId){ 9 | String hql = "from EmployeeModel where id = :employeeId"; 10 | Query q = hbrSession.createQuery(hql); 11 | q.setParameter("employeeId", employeeId); 12 | return (EmployeeModel)q.uniqueResult(); 13 | } 14 | 15 | public static void delete(Session hbrSession, Integer employeeId) throws HibernateException, ConstraintViolationException { 16 | String sqlDeleteUser = "delete from northwind.users where employee_id = :employeeId"; 17 | String sqlDeleteEmployee = "delete from northwind.employees where id = :employeeId"; 18 | 19 | Query queryDeleteUser = hbrSession.createSQLQuery(sqlDeleteUser); 20 | queryDeleteUser.setParameter("employeeId", employeeId); 21 | 22 | Query queryDeleteEmployee = hbrSession.createSQLQuery(sqlDeleteEmployee); 23 | queryDeleteEmployee.setParameter("employeeId", employeeId); 24 | 25 | queryDeleteUser.executeUpdate(); 26 | queryDeleteEmployee.executeUpdate(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/user/LoginModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.user; 2 | 3 | import com.app.model.BaseResponse; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | 6 | @Schema(name = "LoginModel" , description = "Login object") 7 | public class LoginModel { 8 | @Schema(description = "User Name", example = "admin", required=true) 9 | private String username; 10 | 11 | @Schema(description = "Password", example = "password", required=true) 12 | private String password; 13 | 14 | public String getUsername() {return username;} 15 | public void setUsername(String username) {this.username = username;} 16 | 17 | public String getPassword() {return password;} 18 | public void setPassword(String password) {this.password = password;} 19 | 20 | @Schema(name = "LoginResponseModel" , description = "Login response object") 21 | public static class LoginResponse extends BaseResponse { 22 | private UserOutputModel data; 23 | 24 | public LoginResponse(){} 25 | public LoginResponse(UserOutputModel user) { 26 | setMsgType(MessageTypeEnum.SUCCESS); 27 | this.data = user; 28 | } 29 | 30 | public UserOutputModel getData() { return data; } 31 | public void setData(UserOutputModel data) { this.data = data; } 32 | } 33 | } -------------------------------------------------------------------------------- /web-ui/src/api/users-api.ts: -------------------------------------------------------------------------------- 1 | import api from '@/api/api-service'; 2 | import { commitJwtTokenToStore } from '@/shared/utils'; 3 | import { AxiosResponse } from 'axios'; 4 | 5 | export default { 6 | async login(username: string, password: string): Promise { 7 | const resp = await api.post('/authenticate/user', { username, password }); 8 | const respData = resp.data.data; 9 | if (respData) { 10 | commitJwtTokenToStore(respData.token, respData.userId, respData.role, respData.fullName); 11 | return resp; 12 | } 13 | return Promise.reject(resp.data ? resp.data : resp); 14 | }, 15 | 16 | async registerUser(userObj: Record): Promise { 17 | return api.post('/users', userObj); 18 | }, 19 | 20 | async getUsers(page = 1, pageSize = 20, userId?: string, role?: string): Promise { 21 | const qsParams: Record = { }; 22 | if (page) { qsParams.page = page; } 23 | if (pageSize) { qsParams['page-size'] = pageSize; } 24 | if (userId) { qsParams['user-id'] = userId; } 25 | if (role) { qsParams.role = role; } 26 | return api.get('/users', { params: qsParams }); 27 | }, 28 | 29 | async deleteUser(userId: string): Promise { 30 | return api.delete(`/users/${userId}`); 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /web-ui/src/menu/TopNav.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable object-curly-newline */ 2 | export default { 3 | CUSTOMER: [ 4 | { id: '0', label: 'My Profile', icon: 'user', to: '/home/my-profile' }, 5 | { id: '1', label: 'My Orders', icon: 'shopping-bag', to: '/home/my-orders' }, 6 | { id: '2', label: 'My Cart', icon: 'shopping-cart', to: '/home/my-cart' }, 7 | ], 8 | SUPPORT: [ 9 | { id: '0', label: 'My Profile', icon: 'user', to: '/home/my-profile' }, 10 | { id: '1', label: 'Users', icon: 'users', to: '/home/manage/users' }, 11 | { id: '1', label: 'Customers', icon: 'id-badge', to: '/home/manage/customers' }, 12 | { id: '1', label: 'Orders', icon: 'file-group', to: '/home/manage/orders' }, 13 | { id: '1', label: 'Products', icon: 'blocks-group', to: '/home/manage/products' }, 14 | ], 15 | ADMIN: [ 16 | { id: '0', label: 'Dashboard', icon: 'bar-chart', to: '/home/manage/dashboard' }, 17 | { id: '1', label: 'Users', icon: 'users', to: '/home/manage/users' }, 18 | { id: '1', label: 'Customers', icon: 'id-badge', to: '/home/manage/customers' }, 19 | { id: '1', label: 'Orders', icon: 'file-group', to: '/home/manage/orders' }, 20 | { id: '1', label: 'Products', icon: 'blocks-group', to: '/home/manage/products' }, 21 | { id: '1', label: 'Employees', icon: ' certificate', to: '/home/manage/employees' }, 22 | ], 23 | DEFAULT: [], 24 | }; 25 | -------------------------------------------------------------------------------- /web-ui/src/api/order-api.ts: -------------------------------------------------------------------------------- 1 | import api from '@/api/api-service'; 2 | import { AxiosResponse } from 'axios'; 3 | 4 | export default { 5 | async getOrders(page = 1, pageSize = 20, orderId = '', customerId = '', paymentType = '', orderStatus = ''): Promise { 6 | const qsParams: Record = { }; 7 | if (page) { qsParams.page = page; } 8 | if (pageSize) { qsParams['page-size'] = pageSize; } 9 | if (customerId) { qsParams['customer-id'] = customerId; } 10 | if (orderId) { qsParams['order-id'] = orderId; } 11 | if (paymentType) { qsParams['payment-type'] = paymentType; } 12 | if (orderStatus) { qsParams['order-status'] = orderStatus; } 13 | 14 | return api.get('/orders', { params: qsParams }); 15 | }, 16 | 17 | async deleteOrder(orderId: string): Promise { 18 | return api.delete(`/orders/${orderId}`); 19 | }, 20 | 21 | async deleteOrderLine(orderId: string, productId: string): Promise { 22 | return api.delete(`/order-item/${orderId}/${productId}`); 23 | }, 24 | 25 | async deleteOrderLines(orderId: string, commaSeparatedProductIds: string): Promise { 26 | const qsParams: Record = { }; 27 | if (commaSeparatedProductIds) { qsParams['product-ids'] = commaSeparatedProductIds; } 28 | return api.delete(`/order-item/${orderId}`, { params: qsParams }); 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /web-ui/src/shared/chart-options.ts: -------------------------------------------------------------------------------- 1 | export const miniDonutOptions = { 2 | cutoutPercentage: 75, 3 | legend: { 4 | display: false, 5 | }, 6 | tooltips: { 7 | enabled: false, 8 | }, 9 | }; 10 | 11 | export const timeChartOptions = { 12 | legend: { 13 | display: false, 14 | }, 15 | tooltips: { 16 | mode: 'index', 17 | intersect: false, 18 | xPadding: 5, 19 | cornerRadius: 2, 20 | caretSize: 0, 21 | bodyFontSize: 11, 22 | titleFontSize: 11, 23 | }, 24 | scales: { 25 | yAxes: [{ 26 | stacked: true, 27 | ticks: { 28 | beginAtZero: true, 29 | fontSize: 10, 30 | }, 31 | gridLines: { 32 | display: true, 33 | drawBorder: false, 34 | }, 35 | barPercentage: 1, 36 | categoryPercentage: 0.50, 37 | }], 38 | xAxes: [{ 39 | stacked: true, 40 | type: 'time', 41 | ticks: { 42 | display: true, 43 | fontSize: 10, 44 | autoSkip: true, 45 | autoSkipPadding: 5, 46 | maxRotation: 0, 47 | }, 48 | gridLines: { 49 | display: true, 50 | drawBorder: true, 51 | drawTicks: true, 52 | drawOnChartArea: false, 53 | color: '#333', 54 | lineWidth: 1.5, 55 | tickMarkLength: 10, 56 | }, 57 | barPercentage: 0.90, 58 | // categoryPercentage:1 59 | }], 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /web-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mrin-order-processing-ui", 3 | "version": "1.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "scripts": { 7 | "serve": "vue-cli-service serve", 8 | "build": "vue-cli-service build", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "dependencies": { 12 | "axios": "^0.21.1", 13 | "chart.js": "^3.2.0", 14 | "core-js": "^3.11.1", 15 | "mitt": "^2.1.0", 16 | "primeflex": "^2.0.0", 17 | "primeicons": "^4.1.0", 18 | "primevue": "^3.4.0", 19 | "vue": "^3.0.11", 20 | "vue-i18n": "^9.1.6", 21 | "vue-router": "^4.0.6", 22 | "vuex": "^4.0.0", 23 | "vuex-persistedstate": "^4.0.0-beta.3" 24 | }, 25 | "devDependencies": { 26 | "@typescript-eslint/eslint-plugin": "^4.22.0", 27 | "@typescript-eslint/parser": "^4.22.0", 28 | "@vue/cli-plugin-babel": "~4.5.12", 29 | "@vue/cli-plugin-eslint": "~4.5.12", 30 | "@vue/cli-plugin-router": "~4.5.12", 31 | "@vue/cli-plugin-typescript": "~4.5.12", 32 | "@vue/cli-plugin-vuex": "~4.5.12", 33 | "@vue/cli-service": "~4.5.12", 34 | "@vue/compiler-sfc": "^3.0.11", 35 | "@vue/eslint-config-airbnb": "^5.0.2", 36 | "@vue/eslint-config-typescript": "^7.0.0", 37 | "eslint": "^7.25.0", 38 | "eslint-plugin-import": "^2.20.2", 39 | "eslint-plugin-vue": "^7.9.0", 40 | "sass": "^1.32.12", 41 | "sass-loader": "^10.1.1", 42 | "typescript": "~4.2.4" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/stats/DailySaleModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.stats; 2 | 3 | import com.app.model.BaseResponse; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import java.math.BigDecimal; 6 | import java.util.*; 7 | 8 | public class DailySaleModel { 9 | @JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss") 10 | private Date date; 11 | private BigDecimal saleAmount; 12 | private BigDecimal discount; 13 | 14 | //Constructor 15 | public DailySaleModel(Date date, BigDecimal saleAmount, BigDecimal discount) { 16 | this.date = date; 17 | this.saleAmount = saleAmount; 18 | this.discount = discount; 19 | } 20 | 21 | //Getters and Setters 22 | public Date getDate() { return date; } 23 | public void setDate(Date date) { this.date = date; } 24 | 25 | public BigDecimal getSaleAmount() { return saleAmount; } 26 | public void setSaleAmount(BigDecimal saleAmount) { this.saleAmount = saleAmount; } 27 | 28 | public BigDecimal getDiscount() { return discount; } 29 | public void setDiscount(BigDecimal discount) { this.discount = discount; } 30 | 31 | //Response Class 32 | public static class DailySaleResponse extends BaseResponse { 33 | private List list; 34 | 35 | public List getList() {return list; } 36 | public void setList(List list) { this.list = list; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/dao/ProductDao.java: -------------------------------------------------------------------------------- 1 | package com.app.dao; 2 | 3 | import com.app.model.product.ProductModel; 4 | import org.hibernate.*; 5 | import jakarta.validation.ConstraintViolationException; 6 | import java.math.BigDecimal; 7 | 8 | public class ProductDao { 9 | 10 | public static BigDecimal getReferenceCount(Session hbrSession, Integer productId){ 11 | String sql = "Select sum(cnt) from ("+ 12 | " select count(*) as cnt from northwind.cart where product_id = :productId "+ 13 | " union "+ 14 | " select count(*) as cnt from northwind.order_items where product_id = :productId "+ 15 | " )"; 16 | 17 | Query q = hbrSession.createSQLQuery(sql); 18 | q.setParameter("productId", productId); 19 | return (BigDecimal) q.uniqueResult(); 20 | } 21 | 22 | public static ProductModel getById(Session hbrSession, Integer productId){ 23 | String hql = "from ProductModel where id = :productId"; 24 | Query q = hbrSession.createQuery(hql); 25 | q.setParameter("productId", productId); 26 | return (ProductModel)q.uniqueResult(); 27 | } 28 | 29 | public static int delete(Session hbrSession, Integer productId) throws HibernateException, ConstraintViolationException { 30 | Query q = hbrSession.createQuery("delete ProductModel where id = :productId"); 31 | q.setParameter("productId", productId); 32 | return q.executeUpdate(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package com.app.model; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | 5 | public class BaseResponse { 6 | public enum MessageTypeEnum {SUCCESS, ERROR, WARNING, AUTH_FAILED, NO_ACCESS, BAD_TOKEN}; 7 | 8 | protected MessageTypeEnum msgType; 9 | protected String msg; 10 | 11 | @Schema(description = "Can be either SUCCESS, ERROR", example = "SUCCESS", required = true) 12 | public MessageTypeEnum getMsgType() {return msgType;} 13 | public void setMsgType(MessageTypeEnum msgType) {this.msgType = msgType;} 14 | 15 | @Schema(description = "Overall message for the operation", example = "Operation was success", required = true) 16 | public String getMsg() {return msg;} 17 | public void setMsg(String msg) {this.msg = msg;} 18 | 19 | public void setSuccessMessage(String msg){ 20 | this.msgType = MessageTypeEnum.SUCCESS; 21 | this.msg = msg; 22 | } 23 | 24 | public void setErrorMessage(String msg){ 25 | this.msgType = MessageTypeEnum.ERROR; 26 | this.msg = msg; 27 | } 28 | 29 | public void setWarningMessage(String msg){ 30 | this.msgType = MessageTypeEnum.WARNING; 31 | this.msg = msg; 32 | } 33 | 34 | public void setTypeAndMessage(MessageTypeEnum msgType, String msg){ 35 | this.msgType = msgType; 36 | this.msg = msg; 37 | } 38 | public void setTypeAndMessage(MessageTypeEnum msgType, String msg, String usrMsg){ 39 | this.msgType = msgType; 40 | this.msg = msg; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/PageResponse.java: -------------------------------------------------------------------------------- 1 | package com.app.model; 2 | 3 | public class PageResponse extends BaseResponse { 4 | 5 | private int total=0; 6 | private int pageSize=0; 7 | private int currentPage=0; 8 | private int totalPages=0; 9 | private String pagingMsg=""; 10 | public boolean moreItemExist=false; 11 | 12 | // ==== Getters and Setters ==== 13 | public int getTotal() {return total;} 14 | public void setTotal(int total) { 15 | this.total = total; 16 | this.pageSize = total; 17 | this.currentPage = 1; 18 | this.totalPages = 1; 19 | } 20 | 21 | public int getPageSize() {return pageSize;} 22 | public void setPageSize(int pageSize) {this.pageSize = pageSize;} 23 | 24 | public int getCurrentPage() {return currentPage;} 25 | public void setCurrentPage(int currentPage) {this.currentPage = currentPage;} 26 | 27 | public int getTotalPages() {return totalPages;} 28 | public void setTotalPages(int totalPages) {this.totalPages = totalPages;} 29 | 30 | public String getPagingMsg() {return pagingMsg;} 31 | public void setPagingMsg(String pagingMsg) {this.pagingMsg = pagingMsg;} 32 | 33 | public void setPageStats(int total, int pageSize, int currentPage, String pagingMsg){ 34 | if (pageSize>0){ 35 | this.total = total; 36 | this.pageSize = pageSize; 37 | this.currentPage = currentPage; 38 | this.totalPages = (total/pageSize) + ( (total % pageSize==0) ?0:1) ; 39 | this.pagingMsg = pagingMsg; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/api/RelayRuntimeExceptionInResponse.java: -------------------------------------------------------------------------------- 1 | package com.app.api; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | import jakarta.ws.rs.core.MediaType; 6 | import jakarta.ws.rs.core.Response; 7 | import jakarta.ws.rs.ext.ExceptionMapper; 8 | import jakarta.ws.rs.ext.Provider; 9 | import com.app.model.BaseResponse; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * If Rest API controller or Jersey framework throws any exception then this ExceptionMapper 15 | * class will catch the exception and returns JSON error response. (instead of sending some web-server generated html content) 16 | * 17 | */ 18 | @Provider 19 | public class RelayRuntimeExceptionInResponse implements ExceptionMapper { 20 | private static final Logger log = LoggerFactory.getLogger(RelayRuntimeExceptionInResponse.class); 21 | 22 | @Override 23 | public Response toResponse(RuntimeException e) { 24 | BaseResponse resp = new BaseResponse(); 25 | String stackTrace=""; 26 | //stackTrace = Stream.of(e.getStackTrace()).map((a) -> a.toString()).collect(Collectors.joining("' , '", "[ '", "' ]")); 27 | 28 | StringWriter exceptions = new StringWriter(); 29 | e.printStackTrace(new PrintWriter(exceptions)); 30 | stackTrace = exceptions.toString(); 31 | 32 | log.error(stackTrace); 33 | 34 | resp.setErrorMessage( (e.getClass().getCanonicalName() +":-") + (e.getMessage()!=null?e.getMessage():e.toString())); 35 | return Response.status(Response.Status.INTERNAL_SERVER_ERROR) 36 | .entity(resp) 37 | .type(MediaType.APPLICATION_JSON) 38 | .build(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /web-ui/src/components/Legend.vue: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 31 | 32 | 77 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/cart/CartModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.cart; 2 | 3 | import com.app.model.PageResponse; 4 | import javax.persistence.*; 5 | import java.io.Serializable; 6 | import java.util.List; 7 | 8 | @Entity 9 | @Table(name = "cart") 10 | @IdClass(CartModel.CartPrimaryKeys.class) 11 | public class CartModel { 12 | @Id @Column(name = "user_id") private String userId; 13 | @Id @Column(name = "product_id") private Integer productId; 14 | @Column(name = "quantity") private Long quantity; 15 | 16 | //Constructors 17 | public CartModel(){} 18 | public CartModel(String userId, Integer productId, Long quantity) { 19 | this.userId = userId; 20 | this.productId = productId; 21 | this.quantity = quantity; 22 | } 23 | 24 | //Getters and Setters 25 | public String getUserId() {return userId;} 26 | public void setUserId(String userId) { this.userId = userId; } 27 | 28 | public Integer getProductId() { return productId; } 29 | public void setProductId(Integer productId) { this.productId = productId; } 30 | 31 | public Long getQuantity() { return quantity; } 32 | public void setQuantity(Long quantity) { this.quantity = quantity; } 33 | 34 | static class CartPrimaryKeys implements Serializable { 35 | private String userId; 36 | private Integer productId; 37 | 38 | public String getUserId() {return userId;} 39 | public void setUserId(String userId) { this.userId = userId; } 40 | 41 | public Integer getProductId() { return productId; } 42 | public void setProductId(Integer productId) { this.productId = productId; } 43 | } 44 | 45 | //Response for CartModel 46 | public static class CartResponse extends PageResponse { 47 | private List list; 48 | 49 | public List getList() {return list; } 50 | public void setList(List list) { this.list = list; } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /web-api/src/main/webapp/api-docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 25 | 26 | 27 | 36 |
37 | API Docs Powered By: 38 | 39 | RapiDoc 40 | 41 | 42 |
43 |
RENDER STYLE
44 | 45 | 46 |
47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /web-ui/src/shared/utils.ts: -------------------------------------------------------------------------------- 1 | import store from '@/store'; 2 | 3 | export function getLocaleDateDisplay(dt: Date, locale = 'en-US', format: 'date' | 'datetime' = 'datetime'): string { 4 | if (!dt) { 5 | return ''; 6 | } 7 | if (format === 'date') { 8 | return new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'short', day: 'numeric' }).format(dt); 9 | } 10 | return new Intl.DateTimeFormat(locale, { 11 | year: 'numeric', 12 | month: 'short', 13 | day: 'numeric', 14 | hour: 'numeric', 15 | minute: 'numeric', 16 | }).format(dt); 17 | } 18 | 19 | export function getLocaleNumberDisplay(n: number, locale = 'en-US'): string { 20 | if (!n) { 21 | return '0'; 22 | } 23 | return new Intl.NumberFormat(locale, { maximumSignificantDigits: 3 }).format(n); 24 | } 25 | 26 | export function commitJwtTokenToStore(jwt: string, user: string, role: string, userName: string): void { 27 | store.commit('jwt', jwt); 28 | store.commit('jwtTime', new Date().toISOString()); 29 | store.commit('user', user); 30 | store.commit('role', role); 31 | store.commit('userName', userName); 32 | } 33 | 34 | // eslint-disable-next-line @typescript-eslint/ban-types 35 | /* 36 | export function debounce(fn:Function, delay:number): Function { 37 | let timeoutId:unknown = null; 38 | return (...args: unknown[]) => { 39 | clearTimeout(timeoutId as number); 40 | // const args = arguments; 41 | const context = this; // eslint-disable-line @typescript-eslint/no-this-alias 42 | timeoutId = setTimeout(() => { 43 | // fn.apply(that, args); 44 | fn.apply(context, args); 45 | }, delay); 46 | }; 47 | } 48 | */ 49 | 50 | // eslint-disable-next-line @typescript-eslint/ban-types 51 | export function debounce void>(fn: F, delay: number): Function { 52 | let timeoutId: number | null = null; 53 | return function (this: unknown, ...args: unknown[]) { // eslint-disable-line func-names 54 | clearTimeout(timeoutId as number); 55 | timeoutId = window.setTimeout(() => fn.apply(this, args), delay); 56 | } as F; 57 | } 58 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/user/UserModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.user; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import java.io.Serializable; 6 | import java.security.Principal; 7 | import javax.persistence.*; 8 | 9 | @Entity 10 | @Table(name = "users") 11 | public class UserModel implements Serializable, Principal { 12 | @Id 13 | @Column(name = "user_id") private String userId; 14 | private String password; 15 | 16 | @Schema(allowableValues = {"ADMIN", "SUPPORT", "CUSTOMER"}, example="ADMIN") 17 | private String role; 18 | 19 | @Column(name = "employee_id") 20 | private Integer employeeId; 21 | 22 | @Column(name = "customer_id") 23 | private Integer customerId; 24 | 25 | //Constructors 26 | public UserModel(){} 27 | 28 | public UserModel(String userId, String password, String role, Integer employeeId, Integer customerId){ 29 | this.setUserId(userId); 30 | this.setPassword(password); 31 | this.setRole(role); 32 | if (employeeId != null){ 33 | this.setEmployeeId(employeeId); 34 | } else { 35 | this.setCustomerId(customerId); 36 | } 37 | } 38 | 39 | //Getters and Setters 40 | @JsonIgnore // This getter is duplicate of getId but is must for all classes that implements java.security.Principal 41 | public String getName() {return userId;} 42 | 43 | public String getUserId() {return userId;} 44 | public void setUserId(String userId) { this.userId = userId; } 45 | 46 | public String getPassword() { return password; } 47 | public void setPassword(String password) {this.password = password; } 48 | 49 | public String getRole() {return role; } 50 | public void setRole(String role) {this.role = role; } 51 | 52 | public Integer getEmployeeId() { return employeeId; } 53 | public void setEmployeeId(Integer employeeId) { this.employeeId = employeeId; } 54 | 55 | public Integer getCustomerId() { return customerId; } 56 | public void setCustomerId(Integer customerId) { this.customerId = customerId; } 57 | } 58 | -------------------------------------------------------------------------------- /web-ui/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'; 2 | import LoginPage from '@/views/LoginPage.vue'; 3 | import AppShell from '@/components/AppShell.vue'; 4 | import PageContainer from '@/components/PageContainer.vue'; 5 | import Users from '@/views/Users.vue'; 6 | import Customers from '@/views/Customers.vue'; 7 | import Orders from '@/views/Orders.vue'; 8 | import Products from '@/views/Products.vue'; 9 | import Employees from '@/views/Employees.vue'; 10 | import Register from '@/views/Register.vue'; 11 | import Dashboard from '@/views/Dashboard.vue'; 12 | import store from '@/store'; 13 | // import Home from '../views/Home.vue'; 14 | 15 | const routes: Array = [ 16 | { 17 | path: '/', 18 | redirect: () => { 19 | console.log('check Is Authenticated'); 20 | // if authenticated redirect to appshell else to login 21 | return '/login'; 22 | }, 23 | }, 24 | { path: '/login', component: LoginPage, meta: { permitAll: true } }, 25 | { path: '/register', component: Register, meta: { permitAll: true } }, 26 | { 27 | path: '/home', 28 | redirect: '/home/manage/dashboard', 29 | component: AppShell, 30 | children: [ 31 | { 32 | path: 'manage', 33 | redirect: '/home/manage/dashboard', 34 | component: PageContainer, 35 | children: [ 36 | { path: 'dashboard', component: Dashboard }, 37 | { path: 'users', component: Users }, 38 | { path: 'customers', component: Customers }, 39 | { path: 'orders', component: Orders }, 40 | { path: 'products', component: Products }, 41 | { path: 'employees', component: Employees }, 42 | ], 43 | }, 44 | ], 45 | }, 46 | // the default route, when none of the above matches: 47 | { 48 | path: '/:pathMatch(.*)*', 49 | redirect: () => { 50 | if (store.getters.jwt) { // check if authenticated 51 | return '/home'; 52 | } 53 | return '/login'; 54 | }, 55 | }, 56 | ]; 57 | 58 | const router = createRouter({ 59 | history: createWebHistory(process.env.BASE_URL), 60 | routes, 61 | }); 62 | export default router; 63 | -------------------------------------------------------------------------------- /database/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.mrin 6 | jersey-vue 7 | 1.0.0 8 | 9 | com.mrin 10 | database 11 | jar 12 | 1.0.0 13 | 14 | 15 | 16 | com.h2database 17 | h2 18 | ${h2-database-version} 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-jar-plugin 27 | 3.1.0 28 | 29 | 30 | 31 | true 32 | 33 | 34 | 35 | ${project.build.directory}/modules 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-dependency-plugin 42 | 3.1.1 43 | 44 | 45 | package 46 | 47 | copy-dependencies 48 | 49 | 50 | 51 | ${project.build.directory}/modules 52 | 53 | runtime 54 | 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-compiler-plugin 61 | 62 | 11 63 | 11 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/dao/CustomerDao.java: -------------------------------------------------------------------------------- 1 | package com.app.dao; 2 | 3 | import com.app.model.customer.CustomerModel; 4 | import org.hibernate.*; 5 | import jakarta.validation.ConstraintViolationException; 6 | 7 | public class CustomerDao { 8 | 9 | public static CustomerModel getById(Session hbrSession, Integer customerId){ 10 | String hql = "from CustomerModel where id = :customerId"; 11 | Query q = hbrSession.createQuery(hql); 12 | q.setParameter("customerId", customerId); 13 | return (CustomerModel)q.uniqueResult(); 14 | } 15 | 16 | public static void delete(Session hbrSession, Integer customerId) throws HibernateException, ConstraintViolationException { 17 | // Remove customer from all the related tables (do not change the sequence of deleteion else it may give a referal intigrity error) 18 | String sqlDeleteOrderItems = "delete from northwind.order_items where order_id in (select id from northwind.orders where customer_id = :customerId)"; 19 | String sqlDeleteOrders = "delete from northwind.orders where customer_id = :customerId" ; 20 | String sqlDeleteCart = "delete from northwind.cart where user_id in (select user_id from northwind.users where customer_id = :customerId)"; 21 | String sqlDeleteUser = "delete from northwind.users where customer_id = :customerId"; 22 | String sqlDeleteCustomer = "delete from northwind.customers where id = :customerId"; 23 | 24 | Query queryDeleteOrderItems = hbrSession.createSQLQuery(sqlDeleteOrderItems); 25 | queryDeleteOrderItems.setParameter("customerId", customerId); 26 | 27 | Query queryDeleteOrders = hbrSession.createSQLQuery(sqlDeleteOrders); 28 | queryDeleteOrders.setParameter("customerId", customerId); 29 | 30 | Query queryDeleteCart = hbrSession.createSQLQuery(sqlDeleteCart); 31 | queryDeleteCart.setParameter("customerId", customerId); 32 | 33 | Query queryDeleteUser = hbrSession.createSQLQuery(sqlDeleteUser); 34 | queryDeleteUser.setParameter("customerId", customerId); 35 | 36 | Query queryDeleteCustomer = hbrSession.createSQLQuery(sqlDeleteCustomer); 37 | queryDeleteCustomer.setParameter("customerId", customerId); 38 | 39 | queryDeleteOrderItems.executeUpdate(); 40 | queryDeleteOrders.executeUpdate(); 41 | queryDeleteCart.executeUpdate(); 42 | queryDeleteUser.executeUpdate(); 43 | queryDeleteCustomer.executeUpdate(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /database/src/main/java/com/app/DatabaseService.java: -------------------------------------------------------------------------------- 1 | package com.app; 2 | 3 | import java.io.File; 4 | import java.io.InputStream; 5 | import java.sql.*; 6 | import org.h2.tools.Server; 7 | import org.slf4j.*; 8 | 9 | public class DatabaseService { 10 | private static final Logger log = LoggerFactory.getLogger(DatabaseService.class); 11 | 12 | private static final String DB_DRIVER = "org.h2.Driver"; 13 | private static final String DB_CONNECTION = "jdbc:h2:mem:test;INIT=RUNSCRIPT FROM 'classpath:schema.sql'\\;runscript from 'classpath:data.sql'"; 14 | private static final String DB_USER = "sa"; 15 | private static final String DB_PASSWORD = "sa"; 16 | private static Server dbWebServer = null; 17 | 18 | public static void initDB() { 19 | try { 20 | getDBConnection(); 21 | dbWebServer = Server.createWebServer("-webPort","9000", "-webAllowOthers").start(); 22 | // dbWebServer = Server.createTcpServer("-tcpPort", "9092", "-tcpAllowOthers").start(); // If you dont want a web console 23 | } catch (SQLException e) { 24 | e.printStackTrace(); 25 | } 26 | log.info("\n\n *** Database Console ***"+ 27 | "\n DB Console : " + dbWebServer.getURL() + 28 | "\n Driver Class : org.h2.Driver" + 29 | "\n JDBC URL : jdbc:h2:mem:test" + 30 | "\n User : "+DB_USER + 31 | "\n Password : "+DB_PASSWORD + 32 | "\n *** *** *** *** *** \n" 33 | ); 34 | } 35 | 36 | private static Connection getDBConnection() { 37 | Connection dbConnection = null; 38 | try { 39 | Class.forName(DB_DRIVER); 40 | } catch (ClassNotFoundException e) { 41 | log.error(e.getMessage()); 42 | } 43 | try { 44 | dbConnection = DriverManager.getConnection(DB_CONNECTION, DB_USER, DB_PASSWORD); 45 | return dbConnection; 46 | } catch (SQLException e) { 47 | log.error(e.getMessage()); 48 | } 49 | 50 | return dbConnection; 51 | } 52 | 53 | public static File getSqlScriptResourceAsFile( String fileName){ 54 | File file = new File(DatabaseService.class.getClassLoader().getResource(fileName).getFile()); 55 | return file; 56 | } 57 | 58 | public static InputStream getSqlScriptResourceAsStream(String fileName){ 59 | InputStream is = DatabaseService.class.getClassLoader().getResourceAsStream(fileName); 60 | return is; 61 | } 62 | } -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/dao/CartDao.java: -------------------------------------------------------------------------------- 1 | package com.app.dao; 2 | 3 | import com.app.model.cart.CartModel; 4 | import com.app.model.cart.CartViewModel; 5 | import org.hibernate.*; 6 | import jakarta.validation.ConstraintViolationException; 7 | import java.util.List; 8 | 9 | public class CartDao { 10 | public static CartModel getProductsInCart(Session hbrSession, String userId, Integer productId) throws HibernateException, ConstraintViolationException { 11 | Query q = hbrSession.createQuery( "From CartModel where userId = :userId and productId = :productId"); 12 | q.setParameter("userId", userId); 13 | q.setParameter("productId", productId); 14 | return (CartModel)q.uniqueResult(); 15 | } 16 | 17 | public static List getProductsInCart(Session hbrSession, String userId) throws HibernateException, ConstraintViolationException { 18 | Query q = hbrSession.createQuery("From CartModel where userId = :userId"); 19 | q.setParameter("userId", userId); 20 | return q.list(); 21 | } 22 | 23 | public static int updateProductQuantityInCart(Session hbrSession, String userId, Integer productId, Long quantity) throws HibernateException, ConstraintViolationException { 24 | Query q = hbrSession.createQuery("Update CartModel set quantity = :quantity where userId = :userId and productId = :productId"); 25 | q.setParameter("userId", userId); 26 | q.setParameter("productId", productId); 27 | q.setParameter("quantity", quantity); 28 | return q.executeUpdate(); 29 | } 30 | 31 | public static int removeFromCart(Session hbrSession, String userId, Integer productId) throws HibernateException, ConstraintViolationException { 32 | String hql = ""; 33 | if (productId != null){ 34 | hql = "delete CartModel where userId = :userId and productId = :productId"; 35 | } else { 36 | hql = "delete CartModel where userId = :userId"; 37 | } 38 | Query q = hbrSession.createQuery(hql); 39 | q.setParameter("userId", userId); 40 | if (productId != null) { 41 | q.setParameter("productId", productId); 42 | } 43 | return q.executeUpdate(); 44 | } 45 | 46 | public static List listCartItemsOfUser(Session hbrSession, String userId) throws HibernateException, ConstraintViolationException { 47 | Query q = hbrSession.createQuery("From CartViewModel where userId = :userId"); 48 | q.setParameter("userId", userId); 49 | return q.list(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/user/UserViewModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.user; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import org.hibernate.annotations.Immutable; 6 | import javax.persistence.*; 7 | import java.io.Serializable; 8 | import java.security.Principal; 9 | 10 | @Entity 11 | @Immutable //Indicates its a View not a table (cannot be updated) 12 | @Table(name = "user_view") 13 | public class UserViewModel implements Serializable, Principal { 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.IDENTITY) 16 | @Column(name = "user_id") private String userId; 17 | @Column(name = "password") private String password; 18 | @Schema(allowableValues="ADMIN, SUPPORT, CUSTOMER") private String role; 19 | @Column(name = "employee_id") private Integer employeeId; 20 | @Column(name = "customer_id") private Integer customerId; 21 | @Column(name = "full_name") private String fullName; 22 | @Column(name = "email") private String email; 23 | 24 | public UserViewModel(){} 25 | 26 | public UserViewModel(String userId, String role, String fullName, String email, Integer empId, Integer custId){ 27 | this.setUserId(userId); 28 | this.setRole(role); 29 | this.setFullName(fullName); 30 | this.setEmail(email); 31 | this.setEmployeeId(empId); 32 | this.setCustomerId(custId); 33 | } 34 | 35 | @JsonIgnore // This getter is duplicate of getId but is must for all classes that implements java.security.Principal 36 | public String getName() {return userId;} 37 | 38 | public String getUserId() {return userId;} 39 | public void setUserId(String userId) { this.userId = userId; } 40 | 41 | public String getPassword() { return password; } 42 | public void setPassword(String password) { this.password = password; } 43 | 44 | public String getRole() {return role; } 45 | public void setRole(String role) {this.role = role; } 46 | 47 | public Integer getEmployeeId() { return employeeId; } 48 | public void setEmployeeId(Integer employeeId) { this.employeeId = employeeId; } 49 | 50 | public Integer getCustomerId() { return customerId; } 51 | public void setCustomerId(Integer customerId) { this.customerId = customerId; } 52 | 53 | public String getFullName() { return fullName; } 54 | public void setFullName(String fullName) { this.fullName = fullName; } 55 | 56 | public String getEmail() { return email; } 57 | public void setEmail(String email) { this.email = email; } 58 | } 59 | -------------------------------------------------------------------------------- /web-ui/src/components/AppHeader.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 39 | 40 | 88 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/order/OrderItemModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.order; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import javax.persistence.*; 5 | import java.io.Serializable; 6 | import java.math.BigDecimal; 7 | import java.util.Date; 8 | 9 | @Entity 10 | @Table(name = "order_items") 11 | @IdClass(OrderItemModel.OrderItemPrimaryKeys.class) 12 | public class OrderItemModel { 13 | @Id @Column(name = "order_id") private Integer orderId; 14 | @Id @Column(name = "product_id") private Integer productId; 15 | @Column(name = "quantity") private BigDecimal quantity; 16 | @Column(name = "unit_price") private BigDecimal unitPrice; 17 | @Column(name = "discount") private BigDecimal discount; 18 | @Column(name = "order_item_status") private String orderItemStatus; 19 | 20 | @JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss") 21 | @Column(name = "date_allocated") private Date dateAllocated; 22 | 23 | // Getters and Setters 24 | public Integer getOrderId() { return orderId; } 25 | public void setOrderId(Integer orderId) { this.orderId = orderId; } 26 | 27 | public Integer getProductId() { return productId; } 28 | public void setProductId(Integer productId) { this.productId = productId; } 29 | 30 | public BigDecimal getQuantity() { return quantity; } 31 | public void setQuantity(BigDecimal quantity) { this.quantity = quantity; } 32 | 33 | public BigDecimal getUnitPrice() { return unitPrice; } 34 | public void setUnitPrice(BigDecimal unitPrice) { this.unitPrice = unitPrice; } 35 | 36 | public BigDecimal getDiscount() { return discount; } 37 | public void setDiscount(BigDecimal discount) { this.discount = discount; } 38 | 39 | public String getOrderItemStatus() { return orderItemStatus; } 40 | public void setOrderItemStatus(String orderItemStatus) { this.orderItemStatus = orderItemStatus; } 41 | 42 | public Date getDateAllocated() { return dateAllocated; } 43 | public void setDateAllocated(Date dateAllocated) { this.dateAllocated = dateAllocated; } 44 | 45 | static class OrderItemPrimaryKeys implements Serializable { 46 | private Integer orderId; 47 | private Integer productId; 48 | 49 | public Integer getOrderId() { return orderId; } 50 | public void setOrderId(Integer orderId) { this.orderId = orderId; } 51 | 52 | public Integer getProductId() { return productId; } 53 | public void setProductId(Integer productId) { this.productId = productId; } 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /web-api/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | 18 | 19 | 25 | 42 | 43 | 44 | 45 | 46 | 53 | 54 | 55 | 56 | 57 | /index.html 58 | 59 | 60 | 61 | 62 | 400 63 | /page_error.html?code=400 64 | 65 | 66 | 404 67 | /page_error.html?code=404 68 | 69 | 70 | 500 71 | /page_error.html?code=500 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/util/TokenUtil.java: -------------------------------------------------------------------------------- 1 | package com.app.util; 2 | 3 | import com.app.model.user.UserViewModel; 4 | import io.jsonwebtoken.ClaimJwtException; 5 | import io.jsonwebtoken.Claims; 6 | import io.jsonwebtoken.ExpiredJwtException; 7 | import io.jsonwebtoken.Jwts; 8 | import io.jsonwebtoken.SignatureAlgorithm; 9 | import java.util.Date; 10 | import java.time.Instant; 11 | import java.time.temporal.ChronoUnit; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | public class TokenUtil { 16 | 17 | private static Logger log = LoggerFactory.getLogger(TokenUtil.class); 18 | 19 | private static final long VALIDITY_TIME_MS = 2 * 60 * 60 * 1000; // 2 hours validity 20 | private static String secret="mrin"; 21 | 22 | public static String createTokenForUser(UserViewModel userView) { 23 | if (userView == null) { 24 | throw new NullPointerException("username or roles is illegal"); 25 | } 26 | 27 | return Jwts.builder() 28 | .setExpiration(new Date(System.currentTimeMillis() + VALIDITY_TIME_MS)) 29 | .claim("id" , userView.getUserId()) 30 | .claim("role" , userView.getRole()) 31 | .claim("custId" , userView.getCustomerId()) 32 | .claim("empId" , userView.getEmployeeId()) 33 | .claim("name" , userView.getFullName()) 34 | .claim("email" , userView.getEmail()) 35 | .signWith(SignatureAlgorithm.HS256, secret) 36 | .compact(); 37 | } 38 | 39 | public static UserViewModel getUserFromToken(String strToken) throws Exception { 40 | try { 41 | Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(strToken).getBody(); 42 | return new UserViewModel( 43 | (String)claims.get("id"), 44 | (String)claims.get("role"), 45 | (String)claims.get("name"), 46 | (String)claims.get("email"), 47 | (Integer)claims.get("empId"), 48 | (Integer)claims.get("custId") 49 | ) ; 50 | } catch (ExpiredJwtException e){ 51 | log.error("Token Expired"); 52 | return null; 53 | } catch (ClaimJwtException e){ 54 | log.error("Invalid Token"); 55 | return null; 56 | } 57 | } 58 | 59 | public static boolean isExpiringIn30Minutes(String strToken) { 60 | Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(strToken).getBody(); 61 | long remainingMinutes =ChronoUnit.MINUTES.between( Instant.now() , claims.getExpiration().toInstant() ); 62 | return (remainingMinutes < 30)?true:false; 63 | } 64 | 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /web-ui/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | 3 | // Localization 4 | import { createI18n } from 'vue-i18n'; 5 | 6 | // prime Vue 7 | import PrimeVue from 'primevue/config'; 8 | import 'primevue/resources/themes/saga-blue/theme.css'; 9 | import 'primevue/resources/primevue.min.css'; 10 | import 'primeicons/primeicons.css'; 11 | import 'primeflex/primeflex.css'; 12 | 13 | // Prime Components 14 | import Button from 'primevue/button'; 15 | import InputText from 'primevue/inputtext'; 16 | import InputNumber from 'primevue/inputnumber'; 17 | import Password from 'primevue/password'; 18 | import Dropdown from 'primevue/dropdown'; 19 | import DataTable from 'primevue/datatable'; 20 | import Column from 'primevue/column'; 21 | import Dialog from 'primevue/dialog'; 22 | import ConfirmDialog from 'primevue/confirmdialog'; 23 | import ConfirmPopup from 'primevue/confirmpopup'; 24 | import Toast from 'primevue/toast'; 25 | import Message from 'primevue/message'; 26 | import ToastService from 'primevue/toastservice'; 27 | import ConfirmationService from 'primevue/confirmationservice'; 28 | import Sidebar from 'primevue/sidebar'; 29 | import BlockUI from 'primevue/blockui'; 30 | import SelectButton from 'primevue/selectbutton'; 31 | import Menu from 'primevue/menu'; 32 | import Chart from 'primevue/chart'; 33 | 34 | // Event Bus 35 | import mitt from 'mitt'; 36 | 37 | // App, Store and Router 38 | import router from './router'; 39 | import store from './store'; 40 | import App from './App.vue'; 41 | 42 | // Setup Locale translations 43 | const messages = { 44 | en: { message: { hello: 'hello world' } }, 45 | ja: { message: { hello: 'こんにちは、世界' } }, 46 | }; 47 | 48 | const i18n = createI18n({ 49 | locale: 'ja', 50 | fallbackLocale: 'en', 51 | messages, 52 | }); 53 | 54 | const AppEvent = mitt(); 55 | 56 | const app = createApp(App) 57 | .use(i18n) 58 | .use(PrimeVue, { ripple: true }) 59 | .use(ToastService) 60 | .use(ConfirmationService) 61 | .use(store) 62 | .use(router); 63 | 64 | app.component('Button', Button); 65 | app.component('InputText', InputText); 66 | app.component('InputNumber', InputNumber); 67 | app.component('Password', Password); 68 | app.component('Dropdown', Dropdown); 69 | app.component('SelectButton', SelectButton); 70 | app.component('DataTable', DataTable); 71 | app.component('Column', Column); 72 | app.component('Dialog', Dialog); 73 | app.component('ConfirmDialog', ConfirmDialog); 74 | app.component('ConfirmPopup', ConfirmPopup); 75 | app.component('Toast', Toast); 76 | app.component('Message', Message); 77 | app.component('Sidebar', Sidebar); 78 | app.component('BlockUI', BlockUI); 79 | app.component('Menu', Menu); 80 | app.component('Chart', Chart); 81 | 82 | // app.config.globalProperties.$AppEvent = AppEvent; 83 | app.mount('#app'); 84 | app.provide('AppEvent', AppEvent); 85 | // eslint-disable-next-line import/prefer-default-export 86 | export { AppEvent }; 87 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/cart/CartViewModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.cart; 2 | 3 | import com.app.model.PageResponse; 4 | import org.hibernate.annotations.Immutable; 5 | import javax.persistence.*; 6 | import java.util.List; 7 | 8 | @Entity 9 | @Immutable //Indicates its a View not a table (cannot be updated) 10 | @IdClass(CartModel.CartPrimaryKeys.class) 11 | @Table(name = "cart_view") 12 | public class CartViewModel { 13 | 14 | @Id @Column(name = "user_id") private String userId; 15 | @Id @Column(name = "product_id") private Integer productId; 16 | @Column(name = "product_code") private String productCode; 17 | @Column(name = "product_name") private String productName; 18 | @Column(name = "description") private String description; 19 | @Column(name = "quantity") private Long quantity; 20 | @Column(name = "standard_cost") private Long standardCost; 21 | @Column(name = "list_price") private Long listPrice; 22 | 23 | public CartViewModel(){} 24 | public CartViewModel(Integer productId, String productCode, String productName, String description, Long standardCost, Long listPrice) { 25 | this.productId = productId; 26 | this.productCode = productCode; 27 | this.productName = productName; 28 | this.description = description; 29 | this.standardCost = standardCost; 30 | this.listPrice = listPrice; 31 | } 32 | 33 | public String getUserId() {return userId;} 34 | public void setUserId(String userId) { this.userId = userId; } 35 | 36 | public Integer getProductId() { return productId; } 37 | public void setProductId(Integer productId) { this.productId = productId; } 38 | 39 | public String getProductCode() { return productCode; } 40 | public void setProductCode(String productCode) { this.productCode = productCode; } 41 | 42 | public String getProductName() { return productName; } 43 | public void setProductName(String productName) { this.productName = productName; } 44 | 45 | public String getDescription() { return description; } 46 | public void setDescription(String description) { this.description = description; } 47 | 48 | public Long getQuantity() { return quantity; } 49 | public void setQuantity(Long quantity) { this.quantity = quantity; } 50 | 51 | public Long getStandardCost() { return standardCost; } 52 | public void setStandardCost(Long standardCost) { this.standardCost = standardCost; } 53 | 54 | public Long getListPrice() { return listPrice; } 55 | public void setListPrice(Long listPrice) { this.listPrice = listPrice; } 56 | 57 | //Response Class for API 58 | public static class CartViewResponse extends PageResponse { 59 | private List list; 60 | 61 | public List getList() {return list; } 62 | public void setList(List list) { this.list = list; } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/product/ProductModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.product; 2 | 3 | import javax.persistence.*; 4 | import com.app.model.PageResponse; 5 | import io.swagger.v3.oas.annotations.media.Schema; 6 | import java.math.BigDecimal; 7 | import java.util.List; 8 | 9 | @Entity 10 | @Table(name = "products") 11 | public class ProductModel { 12 | @Id @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | @Column(unique = true, nullable = false) private Integer id; 14 | 15 | @Schema(example="601") 16 | @Column(name = "product_code") private String productCode; 17 | @Column(name = "product_name") private String productName; 18 | @Column(name = "description") private String description; 19 | @Column(name = "standard_cost") private BigDecimal standardCost; 20 | @Column(name = "list_price") private BigDecimal listPrice; 21 | @Column(name = "target_level") private Integer targetLevel; 22 | @Column(name = "reorder_level") private Integer reorderLevel; 23 | private Integer discontinued; 24 | 25 | @Schema(allowableValues = {"Camera", "Laptop", "Tablet", "Phone"}, example="Camera") 26 | private String category; 27 | 28 | //Getters and Setters 29 | public Integer getId() { return id; } 30 | public void setId(Integer id) { this.id = id; } 31 | 32 | public String getProductCode() { return productCode; } 33 | public void setProductCode(String productCode) { this.productCode = productCode; } 34 | 35 | public String getProductName() { return productName; } 36 | public void setProductName(String productName) { this.productName = productName; } 37 | 38 | public String getDescription() { return description; } 39 | public void setDescription(String description) { this.description = description; } 40 | 41 | public BigDecimal getStandardCost() { return standardCost; } 42 | public void setStandardCost(BigDecimal standardCost) { this.standardCost = standardCost; } 43 | 44 | public BigDecimal getListPrice() { return listPrice; } 45 | public void setListPrice(BigDecimal listPrice) { this.listPrice = listPrice; } 46 | 47 | public Integer getTargetLevel() { return targetLevel; } 48 | public void setTargetLevel(Integer targetLevel) { this.targetLevel = targetLevel; } 49 | 50 | public Integer getReorderLevel() { return reorderLevel; } 51 | public void setReorderLevel(Integer reorderLevel) { this.reorderLevel = reorderLevel; } 52 | 53 | public Integer getDiscontinued() { return discontinued; } 54 | public void setDiscontinued(Integer discontinued) { this.discontinued = discontinued; } 55 | 56 | public String getCategory() { return category; } 57 | public void setCategory(String category) { this.category = category; } 58 | 59 | public static class ProductResponse extends PageResponse { 60 | private List list; 61 | 62 | public List getList() {return list; } 63 | public void setList(List list) { this.list = list; } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /web-ui/src/components/AppShell.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 71 | 72 | 73 | 103 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/api/controllers/AuthenticationController.java: -------------------------------------------------------------------------------- 1 | package com.app.api.controllers; 2 | 3 | import jakarta.annotation.security.PermitAll; 4 | import jakarta.ws.rs.*; 5 | import jakarta.ws.rs.core.*; 6 | 7 | import com.app.api.BaseController; 8 | import com.app.model.user.*; 9 | import com.app.model.user.LoginModel.LoginResponse; 10 | import io.swagger.v3.oas.annotations.Operation; 11 | import io.swagger.v3.oas.annotations.media.Content; 12 | import io.swagger.v3.oas.annotations.media.Schema; 13 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 14 | import io.swagger.v3.oas.annotations.tags.Tag; 15 | import org.apache.commons.lang3.StringUtils; 16 | 17 | import com.app.model.BaseResponse; 18 | import com.app.util.HibernateUtil; 19 | 20 | import org.slf4j.LoggerFactory; 21 | import org.hibernate.Session; 22 | import org.hibernate.Query; 23 | import com.app.util.TokenUtil; 24 | import org.slf4j.Logger; 25 | 26 | @Tag(name = "1st Authenticate Yourself") 27 | @Path("/authenticate") 28 | @Produces(MediaType.APPLICATION_JSON) 29 | @Consumes(MediaType.APPLICATION_JSON) 30 | public class AuthenticationController extends BaseController { 31 | private static final Logger log = LoggerFactory.getLogger(AuthenticationController.class); 32 | 33 | @POST 34 | @PermitAll 35 | @Path("/user") 36 | @Operation( 37 | summary = "Authenticates user to access Application", 38 | responses = { @ApiResponse(content = @Content(schema = @Schema(implementation = LoginResponse.class)))} 39 | ) 40 | public Response authenticateUser(LoginModel loginModel) { 41 | String uid = loginModel.getUsername(); 42 | String pwd = loginModel.getPassword(); 43 | 44 | BaseResponse resp = new BaseResponse(); 45 | if (StringUtils.isAnyBlank(uid,pwd ) ) { 46 | resp.setErrorMessage("Missing Username or Password"); 47 | resp.setTypeAndMessage(BaseResponse.MessageTypeEnum.AUTH_FAILED, "Missing Username or Password"); 48 | return Response.status(Response.Status.UNAUTHORIZED).entity(resp).build(); 49 | } 50 | 51 | Session hbrSession = HibernateUtil.getSession(); 52 | 53 | String hql = "FROM UserViewModel u WHERE u.userId = :uid and u.password = :pwd"; 54 | Query q = hbrSession.createQuery(hql); 55 | q.setParameter("uid", uid); 56 | q.setParameter("pwd", pwd); 57 | 58 | UserViewModel userView = (UserViewModel)q.uniqueResult(); // can throw org.hibernate.NonUniqueResultException 59 | if (userView!=null){ 60 | String strToken = TokenUtil.createTokenForUser(userView); 61 | UserOutputModel usrOutput = new UserOutputModel( 62 | userView.getUserId(), 63 | userView.getRole(), 64 | userView.getFullName(), 65 | userView.getEmail(), 66 | userView.getEmployeeId(), 67 | userView.getCustomerId(), 68 | strToken 69 | ); 70 | LoginResponse successResp = new LoginResponse(usrOutput); 71 | return Response.status(Response.Status.OK).entity(successResp).build(); 72 | } 73 | 74 | resp.setTypeAndMessage(BaseResponse.MessageTypeEnum.AUTH_FAILED, "Incorrect username/password"); 75 | return Response.status(Response.Status.UNAUTHORIZED).entity(resp).build(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/customer/CustomerModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.customer; 2 | 3 | import com.app.model.PageResponse; 4 | 5 | import javax.persistence.*; 6 | import java.util.List; 7 | 8 | @Entity 9 | @Table(name = "customers") 10 | public class CustomerModel { 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | @Column(unique = true, nullable = false) private Integer id; 14 | @Column(name = "last_name") private String lastName; 15 | @Column(name = "first_name") private String firstName; 16 | private String email; 17 | private String company; 18 | private String phone; 19 | private String address1; 20 | private String address2; 21 | private String city; 22 | private String state; 23 | @Column(name = "postal_code") private String postalCode; 24 | private String country; 25 | 26 | //Constructors 27 | public CustomerModel(){} 28 | public CustomerModel(String lastName, String firstName, String email, String company, String phone, String address1, String address2, String city, String state, String postalCode, String country) { 29 | this.lastName = lastName; 30 | this.firstName = firstName; 31 | this.email = email; 32 | this.company = company; 33 | this.phone = phone; 34 | this.address1 = address1; 35 | this.address2 = address2; 36 | this.city = city; 37 | this.state = state; 38 | this.postalCode = postalCode; 39 | this.country = country; 40 | } 41 | 42 | //Getters and Setters 43 | public Integer getId() { return id; } 44 | public void setId(Integer id) { this.id = id; } 45 | 46 | public String getLastName() { return lastName; } 47 | public void setLastName(String lastName) { this.lastName = lastName; } 48 | 49 | public String getFirstName() { return firstName; } 50 | public void setFirstName(String firstName) { this.firstName = firstName; } 51 | 52 | public String getEmail() { return email; } 53 | public void setEmail(String email) { this.email = email; } 54 | 55 | public String getCompany() { return company; } 56 | public void setCompany(String company) { this.company = company; } 57 | 58 | public String getPhone() { return phone; } 59 | public void setPhone(String phone) { this.phone = phone; } 60 | 61 | public String getAddress1() { return address1; } 62 | public void setAddress1(String address1) { this.address1 = address1; } 63 | 64 | public String getAddress2() { return address2; } 65 | public void setAddress2(String address2) { this.address2 = address2; } 66 | 67 | public String getCity() { return city; } 68 | public void setCity(String city) { this.city = city; } 69 | 70 | public String getState() { return state; } 71 | public void setState(String state) { this.state = state; } 72 | 73 | public String getPostalCode() { return postalCode; } 74 | public void setPostalCode(String postalCode) { this.postalCode = postalCode; } 75 | 76 | public String getCountry() { return country; } 77 | public void setCountry(String country) { this.country = country; } 78 | 79 | public static class CustomerResponse extends PageResponse { 80 | private List list; 81 | 82 | public List getList() {return list; } 83 | public void setList(List list) { this.list = list; } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/user/UserOutputModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.user; 2 | 3 | import com.app.model.BaseResponse; 4 | import com.app.model.PageResponse; 5 | import com.fasterxml.jackson.annotation.JsonIgnore; 6 | import io.swagger.v3.oas.annotations.media.Schema; 7 | import java.io.Serializable; 8 | import java.security.Principal; 9 | import java.util.List; 10 | 11 | public class UserOutputModel implements Serializable, Principal { 12 | private String userId; 13 | 14 | @Schema(allowableValues = {"ADMIN", "SUPPORT", "CUSTOMER"}, example="ADMIN") 15 | private String role; 16 | 17 | private String fullName; 18 | private String email; 19 | private Integer customerId; 20 | private Integer employeeId; 21 | private String token; 22 | 23 | //Constructors 24 | public UserOutputModel(){} 25 | 26 | public UserOutputModel(UserViewModel userView){ 27 | this.setUserId(userView.getUserId()); 28 | this.setRole(userView.getRole()); 29 | this.setFullName(userView.getFullName()); 30 | this.setEmail(userView.getEmail()); 31 | this.setEmployeeId(userView.getEmployeeId()); 32 | this.setCustomerId(userView.getCustomerId()); 33 | this.setToken(""); 34 | } 35 | 36 | public UserOutputModel(String userId, String role, String fullName, String email, Integer empId, Integer custId, String token){ 37 | this.setUserId(userId); 38 | this.setRole(role); 39 | this.setFullName(fullName); 40 | this.setEmail(email); 41 | this.setCustomerId(custId); 42 | this.setEmployeeId(empId); 43 | this.setToken(token); 44 | } 45 | 46 | //Getters & Setters 47 | @JsonIgnore // This getter is duplicate of getId but is must for all classes that implements java.security.Principal 48 | public String getName() {return userId;} 49 | 50 | 51 | public String getUserId() {return userId;} 52 | public void setUserId(String userId) { this.userId = userId; } 53 | 54 | public String getRole() {return role; } 55 | public void setRole(String role) {this.role = role; } 56 | 57 | public String getFullName() { return fullName; } 58 | public void setFullName(String fullName) { this.fullName = fullName; } 59 | 60 | public String getEmail() { return email; } 61 | public void setEmail(String email) { this.email = email; } 62 | 63 | public Integer getCustomerId() { return customerId; } 64 | public void setCustomerId(Integer customerId) { this.customerId = customerId; } 65 | 66 | public Integer getEmployeeId() { return employeeId; } 67 | public void setEmployeeId(Integer employeeId) { this.employeeId = employeeId; } 68 | 69 | public String getToken() {return token; } 70 | public void setToken(String token) {this.token = token; } 71 | 72 | //User Response Classes 73 | public static class UserListResponse extends PageResponse { 74 | private List list = null; 75 | 76 | // ===== Getters & Setters ===== 77 | public List getList() {return list;} 78 | public void setList(List list) {this.list = list;} 79 | } 80 | 81 | public static class UserResponse extends BaseResponse { 82 | private UserOutputModel data = null; 83 | 84 | // ===== Getters & Setters ===== 85 | public UserOutputModel getData() {return data;} 86 | public void setData(UserOutputModel data) {this.data = data;} 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/customer/CustomerUserModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.customer; 2 | 3 | import com.app.model.PageResponse; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import org.hibernate.annotations.Immutable; 6 | import javax.persistence.*; 7 | import java.util.List; 8 | 9 | @Entity 10 | @Immutable //Indicates its a View not a table (cannot be updated) 11 | @Table(name = "customer_user_view") 12 | public class CustomerUserModel { 13 | @Id 14 | @Column(name = "customer_id") private Integer customerId; 15 | @Column(name = "user_id") private String userId; 16 | @Column(name = "password") private String password; 17 | 18 | @Schema(allowableValues = {"ADMIN", "SUPPORT", "CUSTOMER"}, example="ADMIN") 19 | @Column(name = "role") private String role; 20 | 21 | @Column(name = "last_name") private String lastName; 22 | @Column(name = "first_name") private String firstName; 23 | private String email; 24 | private String company; 25 | private String phone; 26 | private String address1; 27 | private String address2; 28 | private String city; 29 | private String state; 30 | @Column(name = "postal_code") private String postalCode; 31 | private String country; 32 | 33 | //Getters and Setters 34 | 35 | public Integer getCustomerId() { return customerId; } 36 | public void setCustomerId(Integer customerId) { this.customerId = customerId; } 37 | 38 | public String getUserId() { return userId; } 39 | public void setUserId(String userId) { this.userId = userId; } 40 | 41 | public String getPassword() { return password; } 42 | public void setPassword(String password) {this.password = password; } 43 | 44 | public String getRole() {return role; } 45 | public void setRole(String role) {this.role = role; } 46 | 47 | public String getLastName() { return lastName; } 48 | public void setLastName(String lastName) { this.lastName = lastName; } 49 | 50 | public String getFirstName() { return firstName; } 51 | public void setFirstName(String firstName) { this.firstName = firstName; } 52 | 53 | public String getEmail() { return email; } 54 | public void setEmail(String email) { this.email = email; } 55 | 56 | public String getCompany() { return company; } 57 | public void setCompany(String company) { this.company = company; } 58 | 59 | public String getPhone() { return phone; } 60 | public void setPhone(String phone) { this.phone = phone; } 61 | 62 | public String getAddress1() { return address1; } 63 | public void setAddress1(String address1) { this.address1 = address1; } 64 | 65 | public String getAddress2() { return address2; } 66 | public void setAddress2(String address2) { this.address2 = address2; } 67 | 68 | public String getCity() { return city; } 69 | public void setCity(String city) { this.city = city; } 70 | 71 | public String getState() { return state; } 72 | public void setState(String state) { this.state = state; } 73 | 74 | public String getPostalCode() { return postalCode; } 75 | public void setPostalCode(String postalCode) { this.postalCode = postalCode; } 76 | 77 | public String getCountry() { return country; } 78 | public void setCountry(String country) { this.country = country; } 79 | 80 | public static class CustomerUserResponse extends PageResponse { 81 | private List list; 82 | 83 | public List getList() {return list; } 84 | public void setList(List list) { this.list = list; } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/user/UserRegistrationModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.user; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | 5 | public class UserRegistrationModel { 6 | @Schema(example = "mickey", required=true) private String userId; 7 | @Schema(example = "mickey", required=true) private String password; 8 | @Schema(example = "CUSTOMER", allowableValues={"ADMIN", "SUPPORT", "CUSTOMER"}, required=true) private String role; 9 | @Schema(example = "Mouse" , required=true) private String lastName; 10 | @Schema(example = "Mickey", required=true) private String firstName; 11 | @Schema(example = "mmouse@example.com") private String email; 12 | @Schema(example = "Disney") private String company; 13 | @Schema(example = "800-200-3490") private String phone; 14 | @Schema(example = "1313 Disneyland Dr") private String address1; 15 | @Schema(example = "Adventure Park") private String address2; 16 | @Schema(example = "Anaheim") private String city; 17 | @Schema(example = "CA") private String state; 18 | @Schema(example = "92802") private String postalCode; 19 | @Schema(example = "USA") private String country; 20 | @Schema(description="applicable for ROLES - ADMIN and SUPPORT only ", example = "Sales") private String department; 21 | @Schema(description="applicable for ROLES - ADMIN and SUPPORT only ", example = "201") private Integer managerId; 22 | 23 | //Getters and Setters 24 | public String getUserId() {return userId;} 25 | public void setUserId(String userId) { this.userId = userId; } 26 | 27 | public String getPassword() { return password; } 28 | public void setPassword(String password) {this.password = password; } 29 | 30 | public String getRole() {return role; } 31 | public void setRole(String role) {this.role = role; } 32 | 33 | public String getLastName() { return lastName; } 34 | public void setLastName(String lastName) { this.lastName = lastName; } 35 | 36 | public String getFirstName() { return firstName; } 37 | public void setFirstName(String firstName) { this.firstName = firstName; } 38 | 39 | public String getEmail() { return email; } 40 | public void setEmail(String email) { this.email = email; } 41 | 42 | public String getCompany() { return company; } 43 | public void setCompany(String company) { this.company = company; } 44 | 45 | public String getPhone() { return phone; } 46 | public void setPhone(String phone) { this.phone = phone; } 47 | 48 | public String getAddress1() { return address1; } 49 | public void setAddress1(String address1) { this.address1 = address1; } 50 | 51 | public String getAddress2() { return address2; } 52 | public void setAddress2(String address2) { this.address2 = address2; } 53 | 54 | public String getCity() { return city; } 55 | public void setCity(String city) { this.city = city; } 56 | 57 | public String getState() { return state; } 58 | public void setState(String state) { this.state = state; } 59 | 60 | public String getPostalCode() { return postalCode; } 61 | public void setPostalCode(String postalCode) { this.postalCode = postalCode; } 62 | 63 | public String getCountry() { return country; } 64 | public void setCountry(String country) { this.country = country; } 65 | 66 | public String getDepartment() { return department; } 67 | public void setDepartment(String department) { this.department = department; } 68 | 69 | public Integer getManagerId() { return managerId; } 70 | public void setManagerId(Integer managerId) { this.managerId = managerId; } 71 | } 72 | -------------------------------------------------------------------------------- /web-ui/src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { createStore, MutationTree, ActionTree, GetterTree } from 'vuex'; 2 | import createPersistedState from 'vuex-persistedstate'; 3 | import axios from 'axios'; 4 | 5 | const localStoragePersist = createPersistedState({ 6 | key: 'app-local', 7 | paths: ['user', 'userName', 'baseUrl', 'lang'], 8 | storage: window.localStorage, 9 | }); 10 | 11 | const sessionStoragePersist = createPersistedState({ 12 | key: 'app-session', 13 | paths: ['role', 'jwt', 'jwtTime'], 14 | storage: window.sessionStorage, 15 | }); 16 | 17 | export interface State { 18 | lang: string; 19 | baseUrl: string; 20 | currentPageTitle: string; 21 | currentHeaderItem: Record; // Includes the Sidemenu (refer app-shell/AppMenu.js) 22 | currentSideNavItem: Record; // Includes the Sidemenu (refer AppMenu.js) 23 | jwt: string; 24 | jwtTime: string; 25 | user: string; 26 | role: string; 27 | userName: string; 28 | email: string; 29 | customerId: string; 30 | employeeId: string; 31 | } 32 | 33 | const getters: GetterTree = { 34 | lang(state: State) { return state.lang; }, 35 | baseUrl(state): string { return state.baseUrl; }, 36 | currentPageTitle(state: State) { return state.currentPageTitle; }, 37 | currentHeaderItem(state: State) { return state.currentHeaderItem; }, 38 | currentSideNavItem(state: State) { return state.currentSideNavItem; }, 39 | jwt(state: State) { return state.jwt; }, 40 | jwtTime(state: State) { return state.jwtTime; }, 41 | user(state: State) { return state.user; }, 42 | role(state: State) { return state.role; }, 43 | userName(state: State) { return state.userName; }, 44 | email(state: State) { return state.email; }, 45 | customerId(state: State) { return state.customerId; }, 46 | employeeId(state: State) { return state.employeeId; }, 47 | }; 48 | 49 | const mutations: MutationTree = { 50 | lang(state: State, payload) { state.lang = payload; }, 51 | baseUrl(state, payload: string) { state.baseUrl = payload; }, 52 | currentPageTitle(state: State, payload: string) { state.currentPageTitle = payload; }, 53 | currentHeaderItem(state: State, payload) { state.currentHeaderItem = payload; }, 54 | currentSideNavItem(state: State, payload) { state.currentSideNavItem = payload; }, 55 | jwt(state: State, payload) { 56 | axios.defaults.headers.common.Authorization = payload; // set axios global default auth header 57 | state.jwt = payload; 58 | }, 59 | jwtTime(state: State, payload) { state.jwtTime = payload; }, 60 | user(state: State, payload: string) { state.user = payload; }, 61 | role(state: State, payload: string) { state.role = payload; }, 62 | userName(state: State, payload: string) { state.userName = payload; }, 63 | email(state: State, payload: string) { state.email = payload; }, 64 | customerId(state: State, payload: string) { state.customerId = payload; }, 65 | employeeId(state: State, payload: string) { state.employeeId = payload; }, 66 | }; 67 | 68 | const actions: ActionTree = {}; 69 | 70 | export default createStore({ 71 | state(): State { 72 | return { 73 | lang: 'en', 74 | baseUrl: '', 75 | currentPageTitle: '', 76 | currentHeaderItem: {}, // Includes the Sidemenu (refer app-shell/AppMenu.js) 77 | currentSideNavItem: {}, // Includes the Sidemenu (refer AppMenu.js) 78 | jwt: '', 79 | jwtTime: '', 80 | user: '', 81 | role: '', 82 | userName: '', 83 | email: '', 84 | customerId: '', 85 | employeeId: '', 86 | }; 87 | }, 88 | getters, 89 | mutations, 90 | actions, 91 | plugins: [localStoragePersist, sessionStoragePersist], 92 | }); 93 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/dao/UserDao.java: -------------------------------------------------------------------------------- 1 | package com.app.dao; 2 | 3 | import com.app.model.user.UserViewModel; 4 | import org.hibernate.*; 5 | 6 | public class UserDao { 7 | 8 | public static UserViewModel getById(Session hbrSession, String userId){ 9 | String hql = "from UserViewModel where userId = :userId"; 10 | Query q = hbrSession.createQuery(hql); 11 | q.setParameter("userId", userId); 12 | return (UserViewModel)q.uniqueResult(); 13 | } 14 | 15 | public static void delete(Session hbrSession, UserViewModel user, boolean deleteRelatedData ){ 16 | 17 | /* To cleanly remove an user 18 | 1. First delete the order-items and order that belong to the associated customer 19 | 2. Then delete the cart that belong to the user 20 | 3. Then delete the user 21 | 4. Finally delete the associated customer 22 | */ 23 | 24 | String sqlDeleteOrderItems = "delete from northwind.order_items where order_id " + 25 | " in (select id from northwind.orders where customer_id " + 26 | " in (select id from northwind.customers where id " + 27 | " in (Select customer_id from northwind.users where user_id = :userId ) " + 28 | " )" + 29 | ")"; 30 | 31 | String sqlDeleteOrders = "delete from northwind.orders where customer_id " + 32 | " in (select id from northwind.customers where id " + 33 | " in (Select customer_id from northwind.users where user_id = :userId) " + 34 | ")"; 35 | 36 | String sqlDeleteCart = "delete from northwind.cart where user_id = :userId"; 37 | String sqlUser = "delete from northwind.users where user_id = :userId"; 38 | String sqlDeleteCustomer = "delete from northwind.customers where id = :customerId"; 39 | String sqlDeleteEmployee = "delete from northwind.employees where id = :employeeId"; 40 | 41 | Query queryDeleteOrderItems = hbrSession.createSQLQuery(sqlDeleteOrderItems); 42 | queryDeleteOrderItems.setParameter("userId", user.getUserId()); 43 | 44 | Query queryDeleteOrders = hbrSession.createSQLQuery(sqlDeleteOrders); 45 | queryDeleteOrders.setParameter("userId", user.getUserId()); 46 | 47 | Query queryDeleteCart = hbrSession.createSQLQuery(sqlDeleteCart); 48 | queryDeleteCart.setParameter("userId", user.getUserId()); 49 | 50 | Query queryDeleteUser = hbrSession.createSQLQuery(sqlUser); 51 | queryDeleteUser.setParameter("userId", user.getUserId()); 52 | 53 | Query queryDeleteCustomer = hbrSession.createSQLQuery(sqlDeleteCustomer); 54 | if (deleteRelatedData && user.getCustomerId() != null) { 55 | queryDeleteCustomer.setParameter("customerId", user.getCustomerId()); 56 | } 57 | 58 | Query queryDeleteEmployee = hbrSession.createSQLQuery(sqlDeleteEmployee); 59 | if (deleteRelatedData && user.getEmployeeId() != null) { 60 | queryDeleteEmployee.setParameter("employeeId", user.getEmployeeId()); 61 | } 62 | if (deleteRelatedData && user.getCustomerId() != null) { 63 | queryDeleteOrderItems.executeUpdate(); 64 | queryDeleteOrders.executeUpdate(); 65 | } 66 | queryDeleteCart.executeUpdate(); 67 | queryDeleteUser.executeUpdate(); 68 | 69 | // Delete associated customer 70 | if (deleteRelatedData && user.getCustomerId() != null) { 71 | queryDeleteCustomer.executeUpdate(); 72 | } 73 | // Delete associated employee 74 | if (deleteRelatedData && user.getEmployeeId() != null) { 75 | queryDeleteEmployee.executeUpdate(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/JerseyApplication.java: -------------------------------------------------------------------------------- 1 | package com.app; 2 | 3 | import jakarta.servlet.ServletContext; 4 | import jakarta.ws.rs.ApplicationPath; 5 | import jakarta.ws.rs.core.Context; 6 | import jakarta.ws.rs.core.UriInfo; 7 | 8 | import com.app.api.HandleInputJsonParseException; 9 | import com.app.util.HibernateUtil; 10 | import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource; 11 | import io.swagger.v3.oas.integration.SwaggerConfiguration; 12 | import io.swagger.v3.oas.models.Components; 13 | import io.swagger.v3.oas.models.OpenAPI; 14 | import io.swagger.v3.oas.models.info.*; 15 | import io.swagger.v3.oas.models.security.SecurityRequirement; 16 | import io.swagger.v3.oas.models.security.SecurityScheme; 17 | import org.glassfish.jersey.media.multipart.MultiPartFeature; 18 | import org.glassfish.jersey.server.ResourceConfig; 19 | import com.app.filters.CORSResponseFilter; 20 | import com.app.filters.AuthorizationRequestFilter; 21 | import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; 22 | 23 | import org.slf4j.*; 24 | import java.util.stream.Collectors; 25 | import java.util.stream.Stream; 26 | 27 | @ApplicationPath("/api") 28 | public class JerseyApplication extends ResourceConfig { 29 | private static final Logger log = LoggerFactory.getLogger(JerseyApplication.class); 30 | 31 | @Context 32 | UriInfo uri; 33 | public JerseyApplication(@Context ServletContext servletContext) { 34 | log.info(String.format("\n\n " + 35 | "*** Web App Initiated *** " + 36 | "\n UI : http://localhost:%s/index.html " + 37 | "\n API : http://localhost:%s/api " + 38 | "\n API Docs: http://localhost:%s/api-docs/index.html \n", 39 | TomcatStarter.port, TomcatStarter.port, TomcatStarter.port 40 | )); 41 | 42 | // Setup OpenAPI 43 | Info info = new Info() 44 | .title("Mrin Order Processing System") 45 | .description("This is a sample server Order processing server. using an In-Memory H2 database") 46 | .contact(new Contact().email("contact-me@example.com")) 47 | .license(new License() 48 | .name("Apache 2.0") 49 | .url("http://www.apache.org/licenses/LICENSE-2.0.html")); 50 | 51 | // SecurityScheme api-key 52 | String securitySchemaName = "api_key"; 53 | SecurityScheme securitySchemeApiKey = new SecurityScheme() 54 | .type(SecurityScheme.Type.APIKEY) 55 | .name("Authorization") 56 | .in(SecurityScheme.In.HEADER); 57 | 58 | OpenApiResource openApiResource = new OpenApiResource(); 59 | // oas.schemaRequirement(securitySchemeApiKey.getName(), securitySchemeApiKey); 60 | 61 | OpenAPI oas = new OpenAPI() 62 | .info(info) 63 | .components(new Components().addSecuritySchemes(securitySchemaName, securitySchemeApiKey)) 64 | .addSecurityItem(new SecurityRequirement().addList(securitySchemaName)); 65 | 66 | SwaggerConfiguration oasConfig = new SwaggerConfiguration() 67 | .openAPI(oas) 68 | .prettyPrint(true) 69 | .resourcePackages(Stream.of("com.app.api").collect(Collectors.toSet())); 70 | openApiResource.setOpenApiConfiguration(oasConfig); 71 | 72 | // Register Features 73 | register(MultiPartFeature.class); 74 | register(JacksonJaxbJsonProvider.class); 75 | 76 | // Register Filters 77 | register(CORSResponseFilter.class); 78 | register(AuthorizationRequestFilter.class); 79 | register(HandleInputJsonParseException.class); 80 | 81 | // Register OpenAPI Resource 82 | register(openApiResource); 83 | 84 | // Register Source Packages 85 | packages("com.app.api"); 86 | 87 | //Configure Hibernate Session 88 | HibernateUtil.getSessionFactory(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/dao/StatsDao.java: -------------------------------------------------------------------------------- 1 | package com.app.dao; 2 | 3 | import com.app.model.stats.CategoryCountModel; 4 | import com.app.model.stats.DailyOrderCountModel; 5 | import com.app.model.stats.DailySaleModel; 6 | import org.hibernate.*; 7 | import java.math.*; 8 | import java.util.*; 9 | 10 | public class StatsDao { 11 | public static List getDailySales(Session hbrSession) { 12 | String sql = "select sum( (unit_price * quantity) - discount) as sale_amount, sum(discount) as discount, order_date as date from NORTHWIND.ORDER_DETAILS " 13 | + " where order_date > DATEADD(DAY, -100 , CURDATE()) " 14 | + " group by DAY_OF_YEAR (order_date) order by order_date desc limit 100"; 15 | 16 | SQLQuery q = hbrSession.createSQLQuery(sql); 17 | List rowList = q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP).list(); 18 | List dailySaleList = new ArrayList<>(); 19 | 20 | for (Object object : rowList) { 21 | Map row = (Map) object; 22 | DailySaleModel dailyRow = new DailySaleModel((Date)row.get("DATE"), (BigDecimal)row.get("SALE_AMOUNT"), (BigDecimal)row.get("DISCOUNT")); 23 | dailySaleList.add(dailyRow); 24 | } 25 | return dailySaleList; 26 | } 27 | 28 | public static List getDailyOrderCount(Session hbrSession) { 29 | String sql = "select count(*) as order_count, order_date as date from NORTHWIND.ORDER_DETAILS " 30 | + " where order_date > DATEADD(DAY, -100 , CURDATE()) " 31 | + " group by DAY_OF_YEAR (order_date) order by order_date desc limit 100"; 32 | 33 | SQLQuery q = hbrSession.createSQLQuery(sql); 34 | List rowList = q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP).list(); 35 | List dailyOrderCount = new ArrayList<>(); 36 | 37 | for (Object object : rowList) { 38 | Map row = (Map) object; 39 | DailyOrderCountModel dailyRow = new DailyOrderCountModel((Date)row.get("DATE"), (BigInteger)row.get("ORDER_COUNT")); 40 | dailyOrderCount.add(dailyRow); 41 | } 42 | return dailyOrderCount; 43 | } 44 | 45 | public static List getOrdersByPaymentType(Session hbrSession) { 46 | String sql = "select count(*) as count, payment_type as category from NORTHWIND.orders where order_date > DATEADD(DAY, -100 , CURDATE()) group by payment_type" ; 47 | 48 | SQLQuery q = hbrSession.createSQLQuery(sql); 49 | List rowList = q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP).list(); 50 | List categoryCountList = new ArrayList<>(); 51 | 52 | for (Object object : rowList) { 53 | Map row = (Map) object; 54 | CategoryCountModel categoryAndCount = new CategoryCountModel((String)row.get("CATEGORY"), (BigInteger)row.get("COUNT")); 55 | categoryCountList.add(categoryAndCount); 56 | } 57 | return categoryCountList; 58 | } 59 | 60 | public static List getOrdersByStatus(Session hbrSession) { 61 | String sql = "select count(*) as count, order_status as category from NORTHWIND.orders where order_date > DATEADD(DAY, -100 , CURDATE()) group by order_status" ; 62 | 63 | SQLQuery q = hbrSession.createSQLQuery(sql); 64 | List rowList = q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP).list(); 65 | List categoryCountList = new ArrayList<>(); 66 | 67 | for (Object object : rowList) { 68 | Map row = (Map) object; 69 | CategoryCountModel categoryAndCount = new CategoryCountModel((String)row.get("CATEGORY"), (BigInteger)row.get("COUNT")); 70 | categoryCountList.add(categoryAndCount); 71 | } 72 | return categoryCountList; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.mrin 5 | jersey-vue 6 | 1.0.0 7 | pom 8 | 9 | 10 | 11 | 12 | database 13 | web-api 14 | 15 | 16 | 17 | 18 | 2.14.1 19 | 10.0.5 20 | 3.0.2 21 | 2.1.9 22 | 2.12.3 23 | 4.3.11.Final 24 | 3.1.0 25 | 3.1.1 26 | 3.12.0 27 | 1.9 28 | 1.4.200 29 | UTF-8 30 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.logging.log4j 41 | log4j-slf4j18-impl 42 | ${log4j-version} 43 | 44 | 45 | org.slf4j 46 | slf4j-api 47 | 2.0.0-alpha1 48 | 49 | 50 | 51 | 52 | org.apache.commons 53 | commons-text 54 | ${commons-text-version} 55 | 56 | 57 | org.apache.commons 58 | commons-lang3 59 | ${commons-lang-version} 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-compiler-plugin 68 | 3.8.0 69 | 70 | 71 | 11 72 | 11 73 | 11 74 | ${project.build.sourceEncoding} 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-resources-plugin 80 | 3.1.0 81 | 82 | ${project.build.sourceEncoding} 83 | 84 | 85 | 86 | 87 | 88 | org.codehaus.mojo 89 | versions-maven-plugin 90 | 2.7 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/employee/EmployeeModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.employee; 2 | 3 | import com.app.model.PageResponse; 4 | import javax.persistence.*; 5 | import java.util.List; 6 | 7 | @Entity 8 | @Table(name = "employees") 9 | public class EmployeeModel { 10 | @Id @GeneratedValue private Integer id; 11 | @Column(name = "last_name") private String lastName; 12 | @Column(name = "first_name") private String firstName; 13 | private String email; 14 | private String avatar; 15 | @Column(name = "job_title") private String jobTitle; 16 | private String department; 17 | @Column(name = "manager_id") private Integer managerId; 18 | private String phone; 19 | private String address1; 20 | private String address2; 21 | private String city; 22 | private String state; 23 | @Column(name = "postal_code") private String postalCode; 24 | private String country; 25 | 26 | //Constructors 27 | public EmployeeModel(){} 28 | public EmployeeModel(String lastName, String firstName, String email, String avatar, String jobTitle, String department, Integer managerId, String phone, String address1, String address2, String city, String state, String postalCode, String country) { 29 | this.lastName = lastName; 30 | this.firstName = firstName; 31 | this.email = email; 32 | this.avatar = avatar; 33 | this.jobTitle = jobTitle; 34 | this.department = department; 35 | this.managerId = managerId; 36 | this.phone = phone; 37 | this.address1 = address1; 38 | this.address2 = address2; 39 | this.city = city; 40 | this.state = state; 41 | this.postalCode = postalCode; 42 | this.country = country; 43 | } 44 | 45 | // Getter and Setters 46 | public Integer getId() { return id; } 47 | public void setId(Integer id) { this.id = id; } 48 | 49 | public String getLastName() { return lastName; } 50 | public void setLastName(String lastName) { this.lastName = lastName; } 51 | 52 | public String getFirstName() { return firstName; } 53 | public void setFirstName(String firstName) { this.firstName = firstName; } 54 | 55 | public String getEmail() { return email; } 56 | public void setEmail(String email) { this.email = email; } 57 | 58 | public String getAvatar() { return avatar; } 59 | public void setAvatar(String avatar) { this.avatar = avatar; } 60 | 61 | public String getJobTitle() { return jobTitle; } 62 | public void setJobTitle(String jobTitle) { this.jobTitle = jobTitle; } 63 | 64 | public String getDepartment() { return department; } 65 | public void setDepartment(String department) { this.department = department; } 66 | 67 | public Integer getManagerId() { return managerId; } 68 | public void setManagerId(Integer managerId) { this.managerId = managerId; } 69 | 70 | public String getPhone() { return phone; } 71 | public void setPhone(String phone) { this.phone = phone; } 72 | 73 | public String getAddress1() { return address1; } 74 | public void setAddress1(String address1) { this.address1 = address1; } 75 | 76 | public String getAddress2() { return address2; } 77 | public void setAddress2(String address2) { this.address2 = address2; } 78 | 79 | public String getCity() { return city; } 80 | public void setCity(String city) { this.city = city; } 81 | 82 | public String getState() { return state; } 83 | public void setState(String state) { this.state = state; } 84 | 85 | public String getPostalCode() { return postalCode; } 86 | public void setPostalCode(String postalCode) { this.postalCode = postalCode; } 87 | 88 | public String getCountry() { return country; } 89 | public void setCountry(String country) { this.country = country; } 90 | 91 | public static class EmployeeResponse extends PageResponse { 92 | private List list; 93 | 94 | public List getList() {return list; } 95 | public void setList(List list) { this.list = list; } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/employee/EmployeeUserModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.employee; 2 | 3 | import com.app.model.PageResponse; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import org.hibernate.annotations.Immutable; 6 | import javax.persistence.*; 7 | import java.util.List; 8 | 9 | @Entity 10 | @Immutable //Indicates its a View not a table (cannot be updated) 11 | @Table(name = "employee_user_view") 12 | public class EmployeeUserModel { 13 | @Id 14 | @Column(name = "employee_id") private Integer employeeId; 15 | @Column(name = "user_id") private String userId; 16 | @Column(name = "password") private String password; 17 | 18 | @Schema(allowableValues = {"ADMIN", "SUPPORT", "CUSTOMER"}, example="ADMIN") 19 | @Column(name = "role") private String role; 20 | 21 | @Column(name = "last_name") private String lastName; 22 | @Column(name = "first_name") private String firstName; 23 | private String email; 24 | private String avatar; 25 | @Column(name = "job_title") private String jobTitle; 26 | private String department; 27 | @Column(name = "manager_id") private Integer managerId; 28 | private String phone; 29 | private String address1; 30 | private String address2; 31 | private String city; 32 | private String state; 33 | @Column(name = "postal_code") private String postalCode; 34 | private String country; 35 | 36 | // Getter and Setters 37 | public Integer getEmployeeId() { return employeeId; } 38 | public void setEmployeeId(Integer employeeId) { this.employeeId = employeeId; } 39 | 40 | public String getUserId() { return userId; } 41 | public void setUserId(String userId) { this.userId = userId; } 42 | 43 | public String getPassword() { return password; } 44 | public void setPassword(String password) {this.password = password; } 45 | 46 | public String getRole() {return role; } 47 | public void setRole(String role) {this.role = role; } 48 | 49 | public String getLastName() { return lastName; } 50 | public void setLastName(String lastName) { this.lastName = lastName; } 51 | 52 | public String getFirstName() { return firstName; } 53 | public void setFirstName(String firstName) { this.firstName = firstName; } 54 | 55 | public String getEmail() { return email; } 56 | public void setEmail(String email) { this.email = email; } 57 | 58 | public String getAvatar() { return avatar; } 59 | public void setAvatar(String avatar) { this.avatar = avatar; } 60 | 61 | public String getJobTitle() { return jobTitle; } 62 | public void setJobTitle(String jobTitle) { this.jobTitle = jobTitle; } 63 | 64 | public String getDepartment() { return department; } 65 | public void setDepartment(String department) { this.department = department; } 66 | 67 | public Integer getManagerId() { return managerId; } 68 | public void setManagerId(Integer managerId) { this.managerId = managerId; } 69 | 70 | public String getPhone() { return phone; } 71 | public void setPhone(String phone) { this.phone = phone; } 72 | 73 | public String getAddress1() { return address1; } 74 | public void setAddress1(String address1) { this.address1 = address1; } 75 | 76 | public String getAddress2() { return address2; } 77 | public void setAddress2(String address2) { this.address2 = address2; } 78 | 79 | public String getCity() { return city; } 80 | public void setCity(String city) { this.city = city; } 81 | 82 | public String getState() { return state; } 83 | public void setState(String state) { this.state = state; } 84 | 85 | public String getPostalCode() { return postalCode; } 86 | public void setPostalCode(String postalCode) { this.postalCode = postalCode; } 87 | 88 | public String getCountry() { return country; } 89 | public void setCountry(String country) { this.country = country; } 90 | 91 | public static class EmployeeUserResponse extends PageResponse { 92 | private List list; 93 | 94 | public List getList() {return list; } 95 | public void setList(List list) { this.list = list; } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/api/MiscController.java: -------------------------------------------------------------------------------- 1 | package com.app.api; 2 | 3 | import jakarta.ws.rs.*; 4 | import jakarta.ws.rs.core.*; 5 | import jakarta.annotation.security.PermitAll; 6 | 7 | import com.app.DatabaseService; 8 | import com.app.model.BaseResponse; 9 | import jakarta.annotation.security.RolesAllowed; 10 | 11 | import com.app.util.Constants.UserRoleConstants; 12 | import io.swagger.v3.oas.annotations.Operation; 13 | import io.swagger.v3.oas.annotations.Parameter; 14 | import io.swagger.v3.oas.annotations.media.Content; 15 | import io.swagger.v3.oas.annotations.media.Schema; 16 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 17 | import io.swagger.v3.oas.annotations.tags.Tag; 18 | 19 | import java.io.BufferedOutputStream; 20 | import java.io.IOException; 21 | import java.io.OutputStream; 22 | import java.nio.file.Paths; 23 | import java.util.zip.ZipEntry; 24 | import java.util.zip.ZipOutputStream; 25 | 26 | @Path("") 27 | @Tag(name = "Misc") 28 | @Consumes(MediaType.APPLICATION_JSON) 29 | @Produces(MediaType.APPLICATION_JSON) 30 | public class MiscController extends BaseController{ 31 | 32 | @GET 33 | @Path("misc/version") 34 | @PermitAll 35 | @Operation( 36 | summary = "Return API Version (Permitted to all)", 37 | responses = { @ApiResponse(content = @Content(schema = @Schema(implementation = BaseResponse.class)))} 38 | ) 39 | public Response getVersion() { 40 | BaseResponse resp = new BaseResponse(); 41 | resp.setSuccessMessage("1.0.0"); 42 | return Response.ok(resp).build(); 43 | } 44 | 45 | @GET 46 | @Path("misc/runtime-error") 47 | @PermitAll 48 | @Operation( 49 | summary = "When an API encounters a runtime-error, its is captured and returned as a JSON response", 50 | responses = { @ApiResponse(content = @Content(schema = @Schema(implementation = BaseResponse.class)))} 51 | ) 52 | public Response getError(@Parameter(description="'true' for creating a runtime-error", example="true") @QueryParam("create-error") boolean createError) { 53 | if (createError) { 54 | throw new NullPointerException("The exception is generated on purpose, to demonstrate when any API encounters a runtime-error, it can be captured and sent as a JSON response, instead of sending a server generated error html page"); 55 | } 56 | BaseResponse resp = new BaseResponse(); 57 | resp.setSuccessMessage("Response when there is no runtime-error"); 58 | return Response.ok(resp).build(); 59 | } 60 | 61 | @GET 62 | @Path("/database-files") 63 | @RolesAllowed({UserRoleConstants.ROLE_ADMIN}) 64 | @Produces(MediaType.APPLICATION_OCTET_STREAM) 65 | @Operation(summary = "Zip files containing SQL scripts to fill this apps database (Example of how to send streaming response)") 66 | public Response downloadSqlScriptFiles() { 67 | StreamingOutput stream = new StreamingOutput() { 68 | @Override 69 | public void write(OutputStream os) throws IOException, WebApplicationException { 70 | String sourceDirPath=""; 71 | try (ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(os))) { 72 | java.nio.file.Path pp = Paths.get(sourceDirPath); 73 | String file1="schema.sql", file2="data.sql"; 74 | 75 | // Put schema.sql entry into zip 76 | ZipEntry zipEntry1 = new ZipEntry(file1); 77 | zos.putNextEntry(zipEntry1); 78 | DatabaseService.getSqlScriptResourceAsStream(file1).transferTo(zos); 79 | zos.closeEntry(); 80 | 81 | // Put data.sql into zip 82 | ZipEntry zipEntry2 = new ZipEntry(file2); 83 | zos.putNextEntry(zipEntry2); 84 | DatabaseService.getSqlScriptResourceAsStream(file2).transferTo(zos); 85 | zos.closeEntry(); 86 | 87 | zos.flush(); 88 | // no need to zos.close(), it will auto-close being inside try block 89 | } catch (Exception e) { 90 | e.printStackTrace(); 91 | throw new WebApplicationException("Unable to create zip file:" + e.getMessage()); 92 | } 93 | } 94 | }; 95 | 96 | return Response 97 | .ok(stream) 98 | .type("application/zip") 99 | .header("Content-Disposition", "attachment; filename=sql_script_files.zip") 100 | .build(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Modular Java, Embedded Tomcat, Fat Jar, Vue JS 2 | Application to demonstrate various parts of a service oriented RESTfull application. 3 | 4 | ## Demo (Heroku Hosted) 5 | Allow about 2-3 mins for the instance to start 6 | - [WebApp](https://modular-java-jersey-vue.herokuapp.com) 7 | - [API Reference (Open API Spec)](https://modular-java-jersey-vue.herokuapp.com/api-docs/index.html) 8 | 9 | ### Technology Stack 10 | Component | Technology 11 | --- | --- 12 | Backend Lang | Java 11 (with modules) 13 | Restfull Framework | Jersey 14 | Container | Tomcat 9 (Embeded Mode) 15 | Server Build Tools | maven 3.5.4 (Creates a Executable Jar with embeded tomcat) 16 | Security | Token Based ([JWT](https://github.com/auth0/java-jwt) ) 17 | REST Spec | [Open API Standard](https://www.openapis.org/) 18 | In Memory DB | H2 19 | Persistence | JPA (Using Hibernate) 20 | Frontend | Vue JS 21 | Client Build Tools | vue-cli, Webpack, yarn 22 | 23 | ## Prerequisites 24 | Ensure you have this installed before proceeding further 25 | - Java 11+ 26 | - Maven 3.5.4+ 27 | - yarn 1.10.1 28 | - node 10.12.0 29 | - vue-cli 3.0.5 30 | 31 | ## Folder Structure 32 | ```bash 33 | PROJECT_FOLDER 34 | │ README.md 35 | │ pom.xml # Parent maven project (contains other sub projects) 36 | │ 37 | └──[database] # Java-Project/Java-Module (contains H2 database related services ) 38 | │ │ pom.xml 39 | │ └──[src] 40 | │ └──[main] 41 | │ └──[java] # java source files 42 | │ └──[resources] 43 | │ schema.sql # Contains sql script to generate database tables and views in H2 44 | │ data.sql # Contains sql script to fill the tables with sample data 45 | │ 46 | └──[web-api] # Java-Project/Java-Module ( The Main WebApp contating RESTfull APIs ) 47 | │ │ pom.xml 48 | │ └──[src] 49 | │ └──[main] 50 | │ └──[java] # java source files 51 | │ └──[resources] 52 | │ └──[webapp] # files/folders under webapp is accessible from web-browser 53 | │ └──[ui] # maven build script would copy web-ui/dist into this folder, to make UI available from the browser 54 | │ └──[api-docs] # contains swagger-ui source for API documentation and try-out 55 | │ 56 | └──[web-ui] # A regular folder that contains VueJS based UI source code 57 | │ │ package.json 58 | │ │ vue.config.js 59 | │ │ package.json 60 | │ └──[node_modules] # files under this is downloaded by 'yarn install' command 61 | │ └──[dist] # VueJs source is compiled, bundled and minified into this folder 62 | │ └──[src] # contains ui source code 63 | ``` 64 | 65 | ### Build Process 66 | - 1st Build Frontend (optional, required only if you make changes to frontend code ) 67 | - Then Build Backend (backend build script will copy UI code build above into itself) 68 | 69 | #### To build frontend (optional step) 70 | ```bash 71 | # run these commands from web-ui folder 72 | 73 | yarn install 74 | yarn build 75 | 76 | ``` 77 | 78 | #### To build backend 79 | ```bash 80 | # run these commands from root folder where top-level pom.xml is present 81 | 82 | # build the web app 83 | mvn clean install 84 | 85 | # Run The App 86 | java -jar ./web-api/target/modules/web-api-1.0.0.jar 87 | ``` 88 | 89 | ##### Once the App is running 90 | App Component | URLs 91 | --- | --- 92 | URL for the web app | http://localhost:8080 or http://localhost:8080/ui/index.html 93 | URL for API Docs | http://localhost:8080/api-docs/index.html 94 | BaseURL for REST APIs| http://localhost:8080/api 95 | 96 | 97 | ### Screenshots 98 | #### Login 99 | 100 | 101 | 102 | 103 | #### Dashboard 104 | 105 | 106 | 107 | 108 | #### API Reference 109 | 110 | 111 | 112 | 113 | ## Backers 114 | Help me to host this app on AWS or Google-Cloud, for everyone to checkout the app. 115 | [[Become a backer](https://opencollective.com/angular-springboot-rest-jwt#backer)] 116 | 117 | 118 | 119 | 120 | ## Sponsors 121 | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/angular-springboot-rest-jwt#sponsor)] 122 | 123 | 124 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/util/HibernateUtil.java: -------------------------------------------------------------------------------- 1 | package com.app.util; 2 | 3 | import com.app.model.cart.CartModel; 4 | import com.app.model.cart.CartViewModel; 5 | import com.app.model.customer.CustomerUserModel; 6 | import com.app.model.employee.EmployeeUserModel; 7 | import com.app.model.order.OrderItemModel; 8 | import com.app.model.user.UserViewModel; 9 | import org.hibernate.Session; 10 | import org.hibernate.SessionFactory; 11 | import org.hibernate.boot.registry.StandardServiceRegistry; 12 | import org.hibernate.boot.registry.StandardServiceRegistryBuilder; 13 | import org.hibernate.cfg.Configuration; 14 | import org.hibernate.service.ServiceRegistry; // Hibernate 4 15 | import com.app.model.user.UserModel; 16 | import com.app.model.customer.CustomerModel; 17 | import com.app.model.employee.EmployeeModel; 18 | import com.app.model.product.ProductModel; 19 | import com.app.model.order.OrderModel; 20 | import com.app.model.order.OrderInfoModel; 21 | 22 | 23 | public class HibernateUtil { 24 | private static SessionFactory sessionFactory; 25 | private static StandardServiceRegistry registry; 26 | 27 | //Hibernate 4 Style 28 | public static SessionFactory getSessionFactory(){ 29 | // setup the session factory 30 | if (sessionFactory == null) { 31 | Configuration configuration = new Configuration(); 32 | 33 | //Connection Props 34 | configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); 35 | configuration.setProperty("hibernate.connection.driver_class", "org.h2.Driver"); 36 | configuration.setProperty("hibernate.connection.url", "jdbc:h2:mem:test;"); 37 | configuration.setProperty("hibernate.connection.release_mode", "auto"); 38 | configuration.setProperty("hibernate.default_schema", "NORTHWIND"); 39 | configuration.setProperty("hibernate.connection.username", "sa"); 40 | configuration.setProperty("hibernate.connection.password", "sa"); 41 | configuration.setProperty("hibernate.show_sql", "true"); 42 | configuration.setProperty("hibernate.format_sql", "true"); 43 | 44 | //Class Mappings 45 | configuration.addAnnotatedClass(UserModel.class); 46 | configuration.addAnnotatedClass(UserViewModel.class); 47 | configuration.addAnnotatedClass(EmployeeModel.class); 48 | configuration.addAnnotatedClass(EmployeeUserModel.class); 49 | configuration.addAnnotatedClass(CustomerModel.class); 50 | configuration.addAnnotatedClass(CustomerUserModel.class); 51 | configuration.addAnnotatedClass(ProductModel.class); 52 | configuration.addAnnotatedClass(OrderModel.class); 53 | configuration.addAnnotatedClass(OrderItemModel.class); 54 | configuration.addAnnotatedClass(OrderInfoModel.class); 55 | configuration.addAnnotatedClass(CartModel.class); 56 | configuration.addAnnotatedClass(CartViewModel.class); 57 | 58 | ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build(); 59 | sessionFactory = configuration.buildSessionFactory(serviceRegistry); 60 | } 61 | return sessionFactory; 62 | } 63 | 64 | public static Session getSession(){ 65 | return getSessionFactory().openSession(); 66 | } 67 | 68 | /* 69 | //Hibernate 5 Style 70 | public static SessionFactory getSessionFactory() { 71 | if (sessionFactory == null) { 72 | 73 | try { 74 | // Create registry ( DB Connection Properties from hibernate.cfg.bak) 75 | registry = new StandardServiceRegistryBuilder().configure().build(); 76 | 77 | // Create MetadataSources and MetaData ( Entity Class Mappings from hibernate.cfg.bak) 78 | MetadataSources sources = new MetadataSources(registry); 79 | Metadata metadata = sources.getMetadataBuilder().build(); 80 | 81 | // Create SessionFactory 82 | sessionFactory = metadata.getSessionFactoryBuilder().build(); 83 | 84 | } 85 | catch (Exception e) { 86 | e.printStackTrace(); 87 | if (registry != null) { 88 | StandardServiceRegistryBuilder.destroy(registry); 89 | } 90 | } 91 | } 92 | return sessionFactory; 93 | } 94 | */ 95 | 96 | public static void shutdown() { 97 | if (registry != null) { 98 | StandardServiceRegistryBuilder.destroy(registry); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/filters/AuthorizationRequestFilter.java: -------------------------------------------------------------------------------- 1 | package com.app.filters; 2 | 3 | import java.io.IOException; 4 | import java.lang.reflect.Method; 5 | import java.util.*; 6 | import java.security.Principal; 7 | import jakarta.annotation.Priority; 8 | import jakarta.annotation.security.*; 9 | import jakarta.servlet.http.HttpServletRequest; 10 | import jakarta.ws.rs.container.*; 11 | import jakarta.ws.rs.core.*; 12 | import jakarta.ws.rs.ext.Provider; 13 | import jakarta.ws.rs.Priorities; 14 | import org.apache.commons.lang3.StringUtils; 15 | import org.slf4j.LoggerFactory; 16 | import com.app.model.user.*; 17 | import com.app.model.BaseResponse; 18 | import com.app.model.BaseResponse.MessageTypeEnum; 19 | import com.app.util.TokenUtil; 20 | import com.app.util.Constants.UserRoleConstants; 21 | 22 | @Provider 23 | @Priority(Priorities.AUTHENTICATION) 24 | public class AuthorizationRequestFilter implements ContainerRequestFilter { 25 | private static final org.slf4j.Logger log = LoggerFactory.getLogger(AuthorizationRequestFilter.class); 26 | 27 | @Context 28 | HttpServletRequest request; 29 | 30 | @Context 31 | ResourceInfo resourceInfo; 32 | 33 | @Override 34 | public void filter(ContainerRequestContext reqContext ) throws IOException { 35 | Method method = resourceInfo.getResourceMethod(); 36 | String className = method.getDeclaringClass().getName(); 37 | String path = reqContext.getUriInfo().getPath(); 38 | BaseResponse resp = new BaseResponse(); 39 | final UserViewModel userView; 40 | 41 | //Allow Access for @PermitAll or OpenAPI Spec 42 | if (reqContext.getMethod().equalsIgnoreCase("OPTIONS") || method.isAnnotationPresent(PermitAll.class) || className.equals("io.swagger.v3.jaxrs2.integration.resources.OpenApiResource")) { 43 | return; 44 | } 45 | 46 | String jwtToken = reqContext.getHeaderString(HttpHeaders.AUTHORIZATION); 47 | if (StringUtils.isBlank(jwtToken)) { 48 | resp.setTypeAndMessage(MessageTypeEnum.BAD_TOKEN, "Empty Token" ); 49 | reqContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).type(MediaType.APPLICATION_JSON).entity(resp).build()); 50 | return; 51 | } 52 | 53 | try { 54 | userView = TokenUtil.getUserFromToken(jwtToken); 55 | if (userView==null) { 56 | resp.setTypeAndMessage(MessageTypeEnum.BAD_TOKEN, "Invalid Token" ); 57 | reqContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).type(MediaType.APPLICATION_JSON).entity(resp).build()); 58 | return; 59 | } 60 | } catch (Exception e) { 61 | log.error("Invalid user token " + e.getMessage()); 62 | resp.setTypeAndMessage(MessageTypeEnum.BAD_TOKEN, "Invalid User" ); 63 | reqContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).type(MediaType.APPLICATION_JSON).entity(resp).build()); 64 | return; 65 | } 66 | 67 | // Store Account in the SecurityContext 68 | final SecurityContext securityContext = reqContext.getSecurityContext(); 69 | reqContext.setSecurityContext(new SecurityContext() { 70 | @Override 71 | public Principal getUserPrincipal() {return userView;} 72 | 73 | @Override 74 | public boolean isUserInRole(String role) {return securityContext.isUserInRole(role);} 75 | 76 | @Override 77 | public boolean isSecure() {return securityContext.isSecure();} 78 | 79 | @Override 80 | public String getAuthenticationScheme() {return "Token-Based-Auth-Scheme";} 81 | }); 82 | 83 | // Everything is permitted for the role "admin" 84 | if (userView.getRole().equalsIgnoreCase(UserRoleConstants.ROLE_ADMIN)){ 85 | return; 86 | } 87 | 88 | //check for roles 89 | if(method.isAnnotationPresent(RolesAllowed.class)) { 90 | RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class); 91 | Set allowedRoleSet = new HashSet<>(Arrays.asList(rolesAnnotation.value() )); 92 | if (allowedRoleSet.contains(userView.getRole())){ 93 | return; 94 | } 95 | if (allowedRoleSet.contains(UserRoleConstants.ROLE_CUSTOMER)){ 96 | // Any endpoint for role "CUSTOMER" is available to all authenticated users (ADMIN, SUPPORT) 97 | return; 98 | } else { 99 | resp.setTypeAndMessage(MessageTypeEnum.NO_ACCESS, "Unauthorized for " + userView.getRole() + " role"); 100 | reqContext.abortWith(Response.status(Response.Status.FORBIDDEN) 101 | .type(MediaType.APPLICATION_JSON) 102 | .entity(resp).build()); 103 | return; 104 | } 105 | } 106 | } 107 | } 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /web-ui/src/views/EmployeeDetails.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 119 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/order/OrderInfoModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.order; 2 | 3 | import com.app.model.PageResponse; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import org.hibernate.annotations.Immutable; 6 | import java.util.*; 7 | import java.math.*; 8 | import javax.persistence.*; 9 | 10 | @Entity 11 | @Immutable //Indicates its a View not a table (cannot be updated) 12 | @Table(name = "order_info") 13 | public class OrderInfoModel { 14 | @Id 15 | @Column(name = "order_id") private Integer orderId; 16 | @Column(name = "customer_id") private Integer customerId; 17 | @Column(name = "order_date") private Date orderDate; 18 | @Column(name = "shipped_date") private Date shippedDate; 19 | @Column(name = "paid_date") private Date paidDate; 20 | @Column(name = "ship_name") private String shipName; 21 | @Column(name = "ship_address1") private String shipAddress1; 22 | @Column(name = "ship_address2") private String shipAddress2; 23 | @Column(name = "ship_city") private String shipCity; 24 | @Column(name = "ship_state") private String shipState; 25 | @Column(name = "ship_postal_code") private String shipPostalCode; 26 | @Column(name = "ship_country") private String shipCountry; 27 | @Column(name = "shipping_fee") private BigDecimal shippingFee; 28 | 29 | @Schema(allowableValues = {"Check","Cash","Card"}) 30 | @Column(name = "payment_type") private String paymentType; 31 | 32 | @Schema(allowableValues = {"On Hold", "Shipped", "Complete", "New"}) 33 | @Column(name = "order_status") private String orderStatus; 34 | 35 | @Column(name = "customer_name") private String customerName; 36 | @Column(name = "customer_phone") private String customerPhone; 37 | @Column(name = "customer_email") private String customerEmail; 38 | @Column(name = "customer_company") private String customerCompany; 39 | 40 | public OrderInfoModel(){} 41 | 42 | 43 | //Getters and Setters 44 | public Integer getOrderId() { return orderId; } 45 | public void setOrderId(Integer orderId) { this.orderId = orderId; } 46 | 47 | public Integer getCustomerId() { return customerId; } 48 | public void setCustomerId(Integer customerId) { this.customerId = customerId; } 49 | 50 | public Date getOrderDate() { return orderDate; } 51 | public void setOrderDate(Date orderDate) { this.orderDate = orderDate; } 52 | 53 | public Date getShippedDate() { return shippedDate; } 54 | public void setShippedDate(Date shippedDate) { this.shippedDate = shippedDate; } 55 | 56 | public Date getPaidDate() { return paidDate; } 57 | public void setPaidDate(Date paidDate) { this.paidDate = paidDate; } 58 | 59 | public String getShipName() { return shipName; } 60 | public void setShipName(String shipName) { this.shipName = shipName; } 61 | 62 | public String getShipAddress1() { return shipAddress1; } 63 | public void setShipAddress1(String shipAddress1) { this.shipAddress1 = shipAddress1; } 64 | 65 | public String getShipAddress2() { return shipAddress2; } 66 | public void setShipAddress2(String shipAddress2) { this.shipAddress2 = shipAddress2; } 67 | 68 | public String getShipCity() { return shipCity; } 69 | public void setShipCity(String shipCity) { this.shipCity = shipCity; } 70 | 71 | public String getShipState() { return shipState; } 72 | public void setShipState(String shipState) { this.shipState = shipState; } 73 | 74 | public String getShipPostalCode() { return shipPostalCode; } 75 | public void setShipPostalCode(String shipPostalCode) { this.shipPostalCode = shipPostalCode; } 76 | 77 | public String getShipCountry() { return shipCountry; } 78 | public void setShipCountry(String shipCountry) { this.shipCountry = shipCountry; } 79 | 80 | public BigDecimal getShippingFee() { return shippingFee; } 81 | public void setShippingFee(BigDecimal shippingFee) { this.shippingFee = shippingFee; } 82 | 83 | public String getPaymentType() { return paymentType; } 84 | public void setPaymentType(String paymentType) { this.paymentType = paymentType; } 85 | 86 | public String getOrderStatus() { return orderStatus; } 87 | public void setOrderStatus(String orderStatus) { this.orderStatus = orderStatus; } 88 | 89 | public String getCustomerName() { return customerName; } 90 | public void setCustomerName(String customerName) { this.customerName = customerName; } 91 | 92 | public String getCustomerPhone() { return customerPhone; } 93 | public void setCustomerPhone(String customerPhone) { this.customerPhone = customerPhone; } 94 | 95 | public String getCustomerEmail() { return customerEmail; } 96 | public void setCustomerEmail(String customerEmail) { this.customerEmail = customerEmail; } 97 | 98 | public String getCustomerCompany() { return customerCompany; } 99 | public void setCustomerCompany(String customerCompany) { this.customerCompany = customerCompany; } 100 | 101 | //API Response class 102 | public static class OrderInfoResponse extends PageResponse { 103 | private List list; 104 | 105 | public List getList() {return list; } 106 | public void setList(List list) { this.list = list; } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /web-ui/src/views/CustomerDetails.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 118 | -------------------------------------------------------------------------------- /web-ui/src/views/ProductDetails.vue: -------------------------------------------------------------------------------- 1 | 67 | 68 | 118 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/api/controllers/StatsController.java: -------------------------------------------------------------------------------- 1 | package com.app.api.controllers; 2 | 3 | import com.app.api.BaseController; 4 | import com.app.dao.StatsDao; 5 | import com.app.model.stats.CategoryCountModel; 6 | import com.app.model.stats.CategoryCountModel.CategoryCountResponse; 7 | import com.app.model.stats.DailySaleModel; 8 | import com.app.model.stats.DailySaleModel.DailySaleResponse; 9 | import com.app.model.stats.DailyOrderCountModel; 10 | import com.app.model.stats.DailyOrderCountModel.DailyOrderCountResponse; 11 | import com.app.util.HibernateUtil; 12 | import io.swagger.v3.oas.annotations.Operation; 13 | import io.swagger.v3.oas.annotations.Parameter; 14 | import io.swagger.v3.oas.annotations.media.Content; 15 | import io.swagger.v3.oas.annotations.media.Schema; 16 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 17 | import io.swagger.v3.oas.annotations.tags.Tag; 18 | import org.hibernate.HibernateException; 19 | import org.hibernate.Session; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import jakarta.annotation.security.RolesAllowed; 24 | import jakarta.validation.ConstraintViolationException; 25 | import jakarta.ws.rs.*; 26 | import jakarta.ws.rs.core.MediaType; 27 | import jakarta.ws.rs.core.Response; 28 | import java.util.List; 29 | 30 | @Path("stats") 31 | @Tag(name = "Statistics") 32 | @Produces(MediaType.APPLICATION_JSON) 33 | @Consumes(MediaType.APPLICATION_JSON) 34 | public class StatsController extends BaseController { 35 | private static Logger log = LoggerFactory.getLogger(StatsController.class); 36 | 37 | @GET 38 | @Path("daily-sale") 39 | @RolesAllowed({"ADMIN", "SUPPORT"}) 40 | @Operation( 41 | summary = "Get Sales by date", 42 | responses = { @ApiResponse(content = @Content(schema = @Schema(implementation = DailySaleResponse.class)))} 43 | ) 44 | public Response getDailySale() { 45 | DailySaleResponse resp = new DailySaleResponse(); 46 | 47 | try { 48 | Session hbrSession = HibernateUtil.getSession(); 49 | hbrSession.beginTransaction(); 50 | List dailySales = StatsDao.getDailySales(hbrSession); 51 | hbrSession.getTransaction().commit(); 52 | resp.setSuccessMessage("Daily Sales"); 53 | resp.setList(dailySales); 54 | return Response.ok(resp).build(); 55 | } catch (HibernateException | ConstraintViolationException e) { 56 | resp.setErrorMessage("Internal Error - " + e.getMessage() + ", " + (e.getCause()!=null? e.getCause().getMessage():"")); 57 | return Response.ok(resp).build(); 58 | } 59 | } 60 | 61 | @GET 62 | @Path("daily-order-count") 63 | @RolesAllowed({"ADMIN", "SUPPORT"}) 64 | @Operation( 65 | summary = "Get Daily order count", 66 | responses = { @ApiResponse(content = @Content(schema = @Schema(implementation = DailyOrderCountResponse.class)))} 67 | ) 68 | public Response getDailyOrderCount() { 69 | DailyOrderCountResponse resp = new DailyOrderCountResponse(); 70 | try { 71 | Session hbrSession = HibernateUtil.getSession(); 72 | hbrSession.beginTransaction(); 73 | List dailyOrderCount = StatsDao.getDailyOrderCount(hbrSession); 74 | hbrSession.getTransaction().commit(); 75 | resp.setSuccessMessage("Daily Order Count"); 76 | resp.setList(dailyOrderCount); 77 | return Response.ok(resp).build(); 78 | } catch (HibernateException | ConstraintViolationException e) { 79 | resp.setErrorMessage("Internal Error - " + e.getMessage() + ", " + (e.getCause()!=null? e.getCause().getMessage():"")); 80 | return Response.ok(resp).build(); 81 | } 82 | } 83 | 84 | @GET 85 | @Path("{orderStats: orders-by-status|orders-by-payment-type}") 86 | @RolesAllowed({"ADMIN", "SUPPORT"}) 87 | @Operation( 88 | summary = "Get Orders by status", 89 | responses = { @ApiResponse(content = @Content(schema = @Schema(implementation = CategoryCountResponse.class)))} 90 | ) 91 | public Response getOrdersByStatus( 92 | @Parameter(schema = @Schema(allowableValues = {"orders-by-status","orders-by-payment-type"}), example="orders-by-status") 93 | @PathParam("orderStats") String orderStats 94 | ) { 95 | CategoryCountResponse resp = new CategoryCountResponse(); 96 | List categoryCountList; 97 | try { 98 | Session hbrSession = HibernateUtil.getSession(); 99 | hbrSession.beginTransaction(); 100 | if (orderStats.equals("orders-by-status")) { 101 | categoryCountList = StatsDao.getOrdersByStatus(hbrSession); 102 | } else { 103 | categoryCountList = StatsDao.getOrdersByPaymentType(hbrSession); 104 | } 105 | hbrSession.getTransaction().commit(); 106 | resp.setSuccessMessage("Orders by status"); 107 | resp.setList(categoryCountList); 108 | return Response.ok(resp).build(); 109 | } catch (HibernateException | ConstraintViolationException e) { 110 | resp.setErrorMessage("Internal Error - " + e.getMessage() + ", " + (e.getCause()!=null? e.getCause().getMessage():"")); 111 | return Response.ok(resp).build(); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/TomcatStarter.java: -------------------------------------------------------------------------------- 1 | package com.app; 2 | 3 | import java.io.*; 4 | import java.nio.file.*; 5 | import java.util.concurrent.*; 6 | import java.net.URISyntaxException; 7 | import com.app.task.RefreshDBTask; 8 | import com.github.lalyos.jfiglet.FigletFont; 9 | import org.apache.catalina.WebResourceRoot; 10 | import org.apache.catalina.WebResourceSet; 11 | import org.apache.catalina.core.StandardContext; 12 | import org.apache.catalina.startup.Tomcat; 13 | import org.apache.catalina.webresources.DirResourceSet; 14 | import org.apache.catalina.webresources.EmptyResourceSet; 15 | import org.apache.catalina.webresources.StandardRoot; 16 | import org.apache.tomcat.util.scan.StandardJarScanner; 17 | import org.slf4j.*; 18 | 19 | public class TomcatStarter { 20 | private static final Logger log = LoggerFactory.getLogger(TomcatStarter.class); 21 | public static int port = 8080; 22 | 23 | private static File getRootFolder() { 24 | try { 25 | File root; 26 | String runningJarPath = TomcatStarter.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath().replaceAll("\\\\", "/"); 27 | int lastIndexOf = runningJarPath.lastIndexOf("/target/"); 28 | if (lastIndexOf < 0) { 29 | root = new File(""); 30 | } else { 31 | root = new File(runningJarPath.substring(0, lastIndexOf)); 32 | } 33 | //System.out.println("application resolved root folder: " + root.getAbsolutePath()); 34 | return root; 35 | } catch (URISyntaxException ex) { 36 | throw new RuntimeException(ex); 37 | } 38 | } 39 | 40 | public static void main(String[] args) throws Exception { 41 | log.info("\n\n\nStarting Tomcat...\n "); 42 | File root = getRootFolder(); 43 | System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true"); 44 | System.setProperty("java.util.logging.SimpleFormatter.format", "%1$tF %1$tT %4$s %2$s %5$s%6$s%n"); 45 | System.setProperty("java.util.logging.ConsoleHandler.level", "WARNING"); 46 | System.setProperty("java.util.logging.FileHandler.level", "WARNING"); 47 | 48 | Tomcat tomcat = new Tomcat(); 49 | Path tempPath = Files.createTempDirectory("tomcat-base-dir"); 50 | tomcat.setBaseDir(tempPath.toString()); 51 | 52 | File webContentFolder = new File(root.getAbsolutePath(), "src/main/webapp/"); 53 | if (!webContentFolder.exists()) { 54 | webContentFolder = Files.createTempDirectory("default-doc-base").toFile(); 55 | } 56 | StandardContext ctx = (StandardContext) tomcat.addWebapp("", webContentFolder.getAbsolutePath()); 57 | 58 | //Disable Jar Scanner 59 | StandardJarScanner scanner=new StandardJarScanner(); 60 | scanner.setScanClassPath(false); 61 | scanner.setScanManifest(false); 62 | scanner.setScanAllDirectories(false); 63 | scanner.setScanAllFiles(false); 64 | 65 | ctx.setJarScanner(scanner); 66 | // Declare an alternative location for your "WEB-INF/classes" dir 67 | // Servlet 3.0 annotation will work 68 | File additionWebInfClassesFolder = new File(root.getAbsolutePath(), "target/classes"); 69 | WebResourceRoot resources = new StandardRoot(ctx); 70 | 71 | WebResourceSet resourceSet; 72 | if (additionWebInfClassesFolder.exists()) { 73 | resourceSet = new DirResourceSet(resources, "/WEB-INF/classes", additionWebInfClassesFolder.getAbsolutePath(), "/"); 74 | //System.out.println("loading WEB-INF resources from as '" + additionWebInfClassesFolder.getAbsolutePath() + "'"); 75 | } else { 76 | resourceSet = new EmptyResourceSet(resources); 77 | } 78 | resources.addPreResources(resourceSet); 79 | ctx.setResources(resources); 80 | 81 | // ASCII ART Banner... 82 | String asciiArt = FigletFont.convertOneLine("Mrin >>>") + " Version 1.0.0"; 83 | log.info(asciiArt); 84 | 85 | log.info("\n\n *** System Variables ***"); 86 | log.info(" user.home :" + System.getProperty("user.home")); 87 | log.info(" user.dir :" + System.getProperty("user.dir")); 88 | log.info(" catalina.home :" + System.getProperty("catalina.home")); 89 | log.info(" catalina.base :" + System.getProperty("catalina.base") +"\n *** *** *** *** *** \n"); 90 | 91 | //Schedule Refresh DB Task 92 | ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1); 93 | RefreshDBTask refreshDBTask = new RefreshDBTask(); //DatabaseService.initDB(); 94 | long period = 1; 95 | TimeUnit timeUnit = TimeUnit.HOURS; 96 | scheduledThreadPool.scheduleAtFixedRate(refreshDBTask, 0, period, timeUnit); 97 | log.info(String.format("\n\nRefreshDB Task Scheduled (The task refreshes the Database every %s %s)", period, timeUnit.toString())); 98 | 99 | //Start Web API Server 100 | String webPort = System.getenv("PORT"); 101 | if(webPort == null || webPort.isEmpty()) { 102 | webPort = "8080"; 103 | } 104 | 105 | tomcat.setPort(Integer.parseInt(webPort)); 106 | tomcat.start(); 107 | tomcat.getConnector(); // Trigger the creation of the default connector 108 | tomcat.getServer().await(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/order/OrderModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.order; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.*; 5 | import javax.persistence.*; 6 | import com.app.model.PageResponse; 7 | import com.fasterxml.jackson.annotation.JsonFormat; 8 | import io.swagger.v3.oas.annotations.media.Schema; 9 | 10 | @Entity 11 | @Table(name = "orders") 12 | public class OrderModel { 13 | @Id @GeneratedValue private Integer id; 14 | @Column(name = "customer_id") private Integer customerId; 15 | 16 | @JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss") 17 | @Column(name = "order_date") private Date orderDate; 18 | 19 | @JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss") 20 | @Column(name = "shipped_date") private Date shippedDate; 21 | 22 | @JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss") 23 | @Column(name = "paid_date") private Date paidDate; 24 | 25 | @Column(name = "ship_name") private String shipName; 26 | @Column(name = "ship_address1") private String shipAddress1; 27 | @Column(name = "ship_address2") private String shipAddress2; 28 | @Column(name = "ship_city") private String shipCity; 29 | @Column(name = "ship_state") private String shipState; 30 | @Column(name = "ship_postal_code") private String shipPostalCode; 31 | @Column(name = "ship_country") private String shipCountry; 32 | @Column(name = "shipping_fee") private BigDecimal shippingFee; 33 | 34 | @Schema(allowableValues = {"Check","Cash","Card"}) 35 | @Column(name = "payment_type") private String paymentType; 36 | 37 | @Schema(allowableValues = {"On Hold", "Shipped", "Complete", "New"}) 38 | @Column(name = "order_status") private String orderStatus; 39 | 40 | //Constructors 41 | public OrderModel(){} 42 | public OrderModel( 43 | Integer id, Integer customerId , Date orderDate , String orderStatus, 44 | Date shippedDate , String shipName , String shipAddress1, String shipAddress2, String shipCity , String shipState, String shipPostalCode, String shipCountry, 45 | BigDecimal shippingFee , String paymentType , Date paidDate 46 | ){ 47 | this.id=id; 48 | this.customerId = customerId ; 49 | this.orderDate = orderDate; 50 | this.orderStatus = orderStatus; 51 | this.shippedDate = shippedDate; 52 | this.shipName = shipName; 53 | this.shipAddress1= shipAddress1; 54 | this.shipAddress2= shipAddress2; 55 | this.shipCity = shipCity; 56 | this.shipState = shipState; 57 | this.shipPostalCode= shipPostalCode; 58 | this.shipCountry = shipCountry; 59 | this.shippingFee = shippingFee; 60 | this.paymentType = paymentType; 61 | this.paidDate = paidDate; 62 | } 63 | 64 | // Getters and Setters 65 | public Integer getId() { return id; } 66 | public void setId(Integer id) { this.id = id; } 67 | 68 | public Integer getCustomerId() { return customerId; } 69 | public void setCustomerId(Integer customerId) { this.customerId = customerId; } 70 | 71 | public Date getOrderDate() { return orderDate; } 72 | public void setOrderDate(Date orderDate) { this.orderDate = orderDate; } 73 | 74 | public Date getShippedDate() { return shippedDate; } 75 | public void setShippedDate(Date shippedDate) { this.shippedDate = shippedDate; } 76 | 77 | public Date getPaidDate() { return paidDate; } 78 | public void setPaidDate(Date paidDate) { this.paidDate = paidDate; } 79 | 80 | public String getShipName() { return shipName; } 81 | public void setShipName(String shipName) { this.shipName = shipName; } 82 | 83 | public String getShipAddress1() { return shipAddress1; } 84 | public void setShipAddress1(String shipAddress1) { this.shipAddress1 = shipAddress1; } 85 | 86 | public String getShipAddress2() { return shipAddress2; } 87 | public void setShipAddress2(String shipAddress2) { this.shipAddress2 = shipAddress2; } 88 | 89 | public String getShipCity() { return shipCity; } 90 | public void setShipCity(String shipCity) { this.shipCity = shipCity; } 91 | 92 | public String getShipState() { return shipState; } 93 | public void setShipState(String shipState) { this.shipState = shipState; } 94 | 95 | public String getShipPostalCode() { return shipPostalCode; } 96 | public void setShipPostalCode(String shipPostalCode) { this.shipPostalCode = shipPostalCode; } 97 | 98 | public String getShipCountry() { return shipCountry; } 99 | public void setShipCountry(String shipCountry) { this.shipCountry = shipCountry; } 100 | 101 | public BigDecimal getShippingFee() {return shippingFee; } 102 | public void setShippingFee(BigDecimal shippingFee) {this.shippingFee = shippingFee; } 103 | 104 | public String getPaymentType() { return paymentType; } 105 | public void setPaymentType(String paymentType) { this.paymentType = paymentType;} 106 | 107 | public String getOrderStatus() { return orderStatus; } 108 | public void setOrderStatus(String orderStatus) { this.orderStatus = orderStatus; } 109 | 110 | public static class OrderResponse extends PageResponse { 111 | private List list; 112 | 113 | public List getList() {return list; } 114 | public void setList(List list) { this.list = list; } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /web-ui/src/api/api-service.ts: -------------------------------------------------------------------------------- 1 | import store from '@/store'; 2 | import { AppEvent } from '@/main'; 3 | import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'; 4 | 5 | export function getBasePath(): string { 6 | const basePath = `${store.getters.baseUrl.replace(/\/$/, '')}/api`; 7 | return basePath; 8 | } 9 | const apiRequestTimeout = 180000; 10 | 11 | const browserTimezone: string = Intl 12 | ? Intl.DateTimeFormat() 13 | .resolvedOptions() 14 | .timeZone 15 | : 'utc'; 16 | 17 | function createAxiosApiService() { 18 | return axios.create({ 19 | baseURL: getBasePath(), 20 | timeout: apiRequestTimeout, 21 | }); 22 | } 23 | 24 | function errorResponseParser(err: AxiosError): Promise { 25 | console.error('[ES] - Error Interceptor: %o', err.response ? err.response : err); 26 | if (err.response) { 27 | if (err.response.status === 401) { 28 | if (err.response.data && err.response.data.msgType === 'AUTH_FAILED') { 29 | store.commit('jwt', ''); 30 | store.commit('jwtTime', ''); 31 | AppEvent.emit('auth-failed', { msg: 'Incorrect username or password' }); 32 | } else if (err.response.data && err.response.data.msgType === 'NO_ACCESS') { 33 | AppEvent.emit('no-access', { msg: 'Operation is not allowed for the given role' }); 34 | } else if (err.response.data && err.response.data.msgType === 'BAD_TOKEN') { 35 | AppEvent.emit('bad-token', { msg: 'Session expired or incorrect token' }); 36 | } 37 | } else { 38 | // AppEvent.emit('server-access-error'); 39 | // TODO : Error Dialog (`Unable to Process the request (${err.response.status})`) 40 | } 41 | return Promise.reject(err); 42 | } 43 | // If there is no response then check if its a timeout issue 44 | if (err.code === 'ECONNABORTED') { 45 | // TODO : Error Dialog (Timeout) 46 | } 47 | 48 | // For all other cases show error 49 | // TODO : Error Dialog 50 | return Promise.reject(err); 51 | } 52 | 53 | export default { 54 | async get(path: string, requestConfig: AxiosRequestConfig = {}): Promise { 55 | const apiService = createAxiosApiService(); 56 | let axiosConfig = { ...requestConfig }; 57 | const headers = { 58 | headers: { 59 | Authorization: store.getters.jwt, 60 | 'timezone-id': browserTimezone, 61 | }, 62 | }; 63 | if (requestConfig.headers && requestConfig.headers instanceof Object) { 64 | axiosConfig.headers = Object.assign(requestConfig.headers, headers.headers); 65 | } else { 66 | axiosConfig = Object.assign(requestConfig, headers); 67 | } 68 | try { 69 | const resp = await apiService.get(path.replace(/^\/|\/$/g, ''), axiosConfig); 70 | return resp; 71 | } catch (err) { 72 | return errorResponseParser(err); 73 | } 74 | }, 75 | 76 | async put(path: string, putData: unknown, requestConfig: AxiosRequestConfig = {}): Promise { 77 | // return Promise.reject('HTTP Method is not allowed'); 78 | const apiService = createAxiosApiService(); 79 | let axiosConfig = { ...requestConfig }; 80 | const headers = { 81 | headers: { 82 | Authorization: store.getters.jwt, 83 | 'timezone-id': browserTimezone, 84 | }, 85 | }; 86 | 87 | if (requestConfig.headers && requestConfig.headers instanceof Object) { 88 | axiosConfig.headers = Object.assign(requestConfig.headers, headers.headers); 89 | } else { 90 | axiosConfig = Object.assign(requestConfig, headers); 91 | } 92 | try { 93 | const resp = await apiService.put(path.replace(/^\/|\/$/g, ''), putData, axiosConfig); 94 | return resp; 95 | } catch (err) { 96 | return errorResponseParser(err); 97 | } 98 | }, 99 | 100 | async post(path: string, postData: unknown, requestConfig: AxiosRequestConfig = {}): Promise { 101 | const apiService = createAxiosApiService(); 102 | let axiosConfig = { ...requestConfig }; 103 | const headers = { 104 | headers: { 105 | Authorization: store.getters.jwt, 106 | 'timezone-id': browserTimezone, 107 | }, 108 | }; 109 | 110 | if (requestConfig?.headers instanceof Object) { 111 | axiosConfig.headers = Object.assign(requestConfig.headers, headers.headers); 112 | } else { 113 | axiosConfig = Object.assign(requestConfig, headers); 114 | } 115 | 116 | if (requestConfig?.headers instanceof Object) { 117 | axiosConfig.headers = Object.assign(requestConfig.headers, headers.headers); 118 | } else { 119 | axiosConfig = Object.assign(requestConfig, headers); 120 | } 121 | try { 122 | const resp = await apiService.post(path.replace(/^\/|\/$/g, ''), postData, axiosConfig); 123 | return resp; 124 | } catch (err) { 125 | return errorResponseParser(err); 126 | } 127 | }, 128 | 129 | async delete(path: string, requestConfig: AxiosRequestConfig = {}): Promise { 130 | const apiService = createAxiosApiService(); 131 | let axiosConfig = { ...requestConfig }; 132 | const headers = { 133 | headers: { 134 | Authorization: store.getters.jwt, 135 | 'timezone-id': browserTimezone, 136 | }, 137 | }; 138 | 139 | if (requestConfig?.headers instanceof Object) { 140 | axiosConfig.headers = Object.assign(requestConfig.headers, headers.headers); 141 | } else { 142 | axiosConfig = Object.assign(requestConfig, headers); 143 | } 144 | 145 | try { 146 | const resp = await apiService.delete(path.replace(/^\/|\/$/g, ''), axiosConfig); 147 | return resp; 148 | } catch (err) { 149 | return errorResponseParser(err); 150 | } 151 | }, 152 | }; 153 | -------------------------------------------------------------------------------- /web-api/src/main/java/com/app/model/order/OrderWithNestedDetailModel.java: -------------------------------------------------------------------------------- 1 | package com.app.model.order; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.*; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import io.swagger.v3.oas.annotations.media.Schema; 7 | 8 | /** 9 | * OrderWithNestedDetails contains extra details of the order 10 | * Such as Customer Info (Name, Company , Email) 11 | * and List of Products in each order 12 | */ 13 | public class OrderWithNestedDetailModel extends OrderModel { 14 | private String customerName; 15 | private String customerEmail; 16 | private String customerCompany; 17 | private BigDecimal orderTotal; 18 | private List orderLine; 19 | 20 | public OrderWithNestedDetailModel(){} 21 | public OrderWithNestedDetailModel( 22 | Integer orderId, 23 | Date orderDate, 24 | String orderStatus, 25 | Date shippedDate, 26 | String shipName, 27 | String shipAddress1, 28 | String shipAddress2 , 29 | String shipCity, 30 | String shipState, 31 | String shipPostalCode, 32 | String shipCountry, 33 | BigDecimal shippingFee, 34 | Integer customerId, 35 | String customerName, 36 | String customerEmail, 37 | String customerCompany, 38 | String paymentType, 39 | Date paidDate 40 | ){ 41 | super(orderId, customerId, orderDate, orderStatus, shippedDate ,shipName, shipAddress1,shipAddress2, shipCity, shipState, shipPostalCode, shipCountry, shippingFee, paymentType, paidDate); 42 | this.customerName = customerName; 43 | this.customerEmail = customerEmail; 44 | this.customerCompany = customerCompany; 45 | this.orderLine = new ArrayList(); 46 | } 47 | 48 | public void addOrderLine(int productId, String productCode, String productName, String category, BigDecimal quantity, BigDecimal unitPrice, BigDecimal discount, Date dateAllocated, String orderItemStatus){ 49 | OrderLine line = new OrderLine(productId, productCode, productName, category, quantity, unitPrice, discount, dateAllocated, orderItemStatus); 50 | this.orderLine.add(line); 51 | } 52 | 53 | //Getters and Setters 54 | public String getCustomerName() { return customerName; } 55 | public void setCustomerName(String customerName) { this.customerName = customerName; } 56 | 57 | public String getCustomerEmail() { return customerEmail; } 58 | public void setCustomerEmail(String customerEmail) { this.customerEmail = customerEmail; } 59 | 60 | public String getCustomerCompany() { return customerCompany; } 61 | public void setCustomerCompany(String customerCompany) { this.customerCompany = customerCompany; } 62 | 63 | public BigDecimal getOrderTotal() { return orderTotal; } 64 | public void setOrderTotal(BigDecimal orderTotal) { this.orderTotal = orderTotal; } 65 | 66 | public List getOrderLine() { return orderLine; } 67 | public void setOrderLine(List orderLine) { this.orderLine = orderLine; } 68 | } 69 | 70 | class OrderLine { 71 | private int productId; 72 | private String productCode; 73 | private String productName; 74 | private String category; 75 | private BigDecimal quantity; 76 | private BigDecimal unitPrice; 77 | private BigDecimal discount; 78 | 79 | @Schema(example="2021-01-28T11:55:00", pattern="yyyy-MM-dd'T'HH:mm:ss") 80 | @JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss") 81 | private Date dateAllocated; 82 | 83 | @Schema(allowableValues = {"On Order", "Allocated", "No Stock"}, example="On Order") 84 | private String orderItemStatus; 85 | 86 | 87 | public OrderLine(int productId, String productCode, String productName, String category, BigDecimal quantity, BigDecimal unitPrice, BigDecimal discount, Date dateAllocated, String orderItemStatus){ 88 | this.productId = productId; 89 | this.productCode = productCode; 90 | this.productName = productName; 91 | this.category = category; 92 | this.quantity = quantity; 93 | this.unitPrice = unitPrice; 94 | this.discount = discount; 95 | this.dateAllocated= dateAllocated; 96 | this.orderItemStatus=orderItemStatus; 97 | } 98 | 99 | public int getProductId() { return productId; } 100 | public void setProductId(int productId) { this.productId = productId; } 101 | 102 | public String getProductCode() { return productCode; } 103 | public void setProductCode(String productCode) { this.productCode = productCode; } 104 | 105 | public String getProductName() { return productName; } 106 | public void setProductName(String productName) { this.productName = productName; } 107 | 108 | public String getCategory() { return category; } 109 | public void setCategory(String category) { this.category = category; } 110 | 111 | public BigDecimal getQuantity() { return quantity; } 112 | public void setQuantity(BigDecimal quantity) { this.quantity = quantity; } 113 | 114 | public BigDecimal getUnitPrice() { return unitPrice; } 115 | public void setUnitPrice(BigDecimal unitPrice) { this.unitPrice = unitPrice; } 116 | 117 | public BigDecimal getDiscount() { return discount; } 118 | public void setDiscount(BigDecimal discount) { this.discount = discount; } 119 | 120 | public Date getDateAllocated() { return dateAllocated; } 121 | public void setDateAllocated(Date dateAllocated) { this.dateAllocated = dateAllocated; } 122 | 123 | public String getOrderItemStatus() { return orderItemStatus; } 124 | public void setOrderItemStatus(String orderItemStatus) { this.orderItemStatus = orderItemStatus; } 125 | } 126 | -------------------------------------------------------------------------------- /web-ui/src/views/Products.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 149 | -------------------------------------------------------------------------------- /web-ui/src/views/LoginPage.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 113 | 114 | 157 | --------------------------------------------------------------------------------