diff --git a/.gitignore b/.gitignore
index 0fff901..69bc07d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -93,4 +93,5 @@ bin/
/discovery-server/target/
/api-gateway/target/
/logging-service/target/
+/user-service/target/
/frontend/build/
diff --git a/README.md b/README.md
index 7dd10b1..54c0ab8 100644
--- a/README.md
+++ b/README.md
@@ -13,5 +13,22 @@

-## Stack
+## Backend build
+
+
+# Backend structure
+
+
+
+# Database - MySQL & MongoDB
+
+
+
+# Discover Service in the MicroService Architecture
+
+
+
+# GANTT diagram
+
+
diff --git a/api-contract/src/main/resources/api-contract.yaml b/api-contract/src/main/resources/api-contract.yaml
index d8a8da2..df884ad 100644
--- a/api-contract/src/main/resources/api-contract.yaml
+++ b/api-contract/src/main/resources/api-contract.yaml
@@ -7,12 +7,14 @@ info:
email: csabavadasz79@gmail.com
servers:
- url: 'http://localhost:8080'
+ - url: 'https://csaba79coder.itps.hu'
tags:
- name: government-representative
- name: government
- name: menu
- name: index
- name: log
+ - name: user
paths:
/:
@@ -268,6 +270,7 @@ paths:
description: Expiration date header
schema:
type: string
+
/{languageShortName}/api/admin/governments/menu:
get:
tags:
@@ -304,11 +307,12 @@ paths:
description: Expiration date header
schema:
type: string
+
/api/admin/logging-service/log:
get:
tags:
- log
- summary: Get all log
+ summary: Get all logs
operationId: renderAllLogs
responses:
'200':
@@ -353,6 +357,120 @@ paths:
'405':
description: Invalid input
+ /api/admin/user-service/users:
+ get:
+ tags:
+ - user
+ summary: Get all Users
+ operationId: renderAllUsers
+ responses:
+ '200':
+ description: Successful operation
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/UserModel'
+ '400':
+ description: Bad request
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: User not found
+ '405':
+ description: Validation exception
+
+ post:
+ tags:
+ - user
+ summary: Add new user in a service
+ description: Add new user in a service and save to database
+ operationId: addNewUser
+ requestBody:
+ description: Create a user and store it
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/UserNewModel'
+ required: true
+ responses:
+ '201':
+ description: Successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/UserModel'
+ '405':
+ description: Invalid input
+
+ /api/admin/user-service/users/{userId}:
+ get:
+ tags:
+ - user
+ summary: Get user by Id
+ operationId: renderUserById
+ parameters:
+ - name: userId
+ in: path
+ description: ID of the user to update
+ required: true
+ schema:
+ type: string
+ format: uuid
+ example: '3a8ea9f1-1a95-4caf-932f-2f988052933b'
+ responses:
+ '200':
+ description: Successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/UserModel'
+ '400':
+ description: Bad request
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: User not found
+ '405':
+ description: Validation exception
+
+ put:
+ tags:
+ - user
+ summary: Modify an existing user in a service
+ description: Modify an existing user in a service and save to database
+ operationId: modifyUserById
+ parameters:
+ - name: userId
+ in: path
+ description: ID of the user to update
+ required: true
+ schema:
+ type: string
+ format: uuid
+ example: '3a8ea9f1-1a95-4caf-932f-2f988052933b'
+ requestBody:
+ description: Create a user and store it
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/UserModel'
+ required: true
+ responses:
+ '201':
+ description: Successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/UserModifyModel'
+ '405':
+ description: Invalid input
+
components:
schemas:
RepresentativeAdminModel:
@@ -587,4 +705,76 @@ components:
level:
type: string
serviceName:
- type: string
\ No newline at end of file
+ type: string
+
+ UserNewModel:
+ type: object
+ properties:
+ username:
+ type: string
+ password:
+ type: string
+ repeatPassword:
+ type: string
+ email:
+ type: string
+ required:
+ - username
+ - password
+ - repeatPassword
+ - email
+
+ UserModel:
+ type: object
+ properties:
+ id:
+ description: Id of the User
+ type: string
+ format: uuid
+ example: '3a8ea9f1-1a95-4caf-932f-2f988052933b'
+ createdAt:
+ description: Creation time of User entity
+ type: string
+ format: datetime
+ example: '2019-01-21T05:47:08.644'
+ updatedAt:
+ description: Updated time of User entity
+ type: string
+ format: datetime
+ example: '2019-01-21T05:47:08.644'
+ createdBy:
+ description: Admin id who created the User entity
+ type: string
+ format: uuid
+ example: '3a8ea9f1-1a95-4caf-932f-2f988052933b'
+ updatedBy:
+ description: User/Admin id who updated the User entity and with setting availability to DELETE, will achieve a soft delete
+ type: string
+ format: uuid
+ example: '3a8ea9f1-1a95-4caf-932f-2f988052933b'
+ username:
+ type: string
+ email:
+ type: string
+ role:
+ $ref: '#/components/schemas/Role'
+ availability:
+ $ref: '#/components/schemas/Availability'
+ UserModifyModel:
+ type: object
+ properties:
+ username:
+ type: string
+ email:
+ type: string
+ role:
+ $ref: '#/components/schemas/Role'
+ availability:
+ $ref: '#/components/schemas/Availability'
+
+ Role:
+ type: string
+ enum:
+ - ADMIN
+ - USER
+ - SUPER_ADMIN
\ No newline at end of file
diff --git a/api-gateway/src/main/resources/application.properties b/api-gateway/src/main/resources/application.properties
index 09693c2..fef5fd6 100644
--- a/api-gateway/src/main/resources/application.properties
+++ b/api-gateway/src/main/resources/application.properties
@@ -34,4 +34,7 @@ spring.cloud.gateway.routes[3].id=logging-service
spring.cloud.gateway.routes[3].uri=lb://logging-service
spring.cloud.gateway.routes[3].predicates[0]=Path=/api/admin/logging-service/**
-
+# User Service Route:
+spring.cloud.gateway.routes[4].id=user-service
+spring.cloud.gateway.routes[4].uri=lb://user-service
+spring.cloud.gateway.routes[4].predicates[0]=Path=/api/admin/user-service/**
diff --git a/api-gateway/target/api-gateway-3.0.2.jar b/api-gateway/target/api-gateway-3.0.2.jar
new file mode 100644
index 0000000..23abe84
Binary files /dev/null and b/api-gateway/target/api-gateway-3.0.2.jar differ
diff --git a/api-gateway/target/classes/application.properties b/api-gateway/target/classes/application.properties
new file mode 100644
index 0000000..fef5fd6
--- /dev/null
+++ b/api-gateway/target/classes/application.properties
@@ -0,0 +1,40 @@
+server.port=8080
+eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka
+spring.application.name=api-gateway
+
+spring.cloud.gateway.discovery.locator.enabled=true
+spring.cloud.gateway.discovery.locator.lower-case-service-id=true
+
+logging.level.root=info
+logging.level.org.springframework.cloud.gateway.route.RouteDefinitionLocator=info
+logging.level.org.springframework.cloud.gateway=trace
+
+# Government Service Route:
+spring.cloud.gateway.routes[0].id=government-service
+spring.cloud.gateway.routes[0].uri=lb://government-service
+#spring.cloud.gateway.routes[0].predicates[0]=Path=/**
+spring.cloud.gateway.routes[0].predicates[0]=Path=/{languageShortName}/api/admin/**
+
+# TODO do not forget the government!
+# :languageShortName/api/admin/governments
+
+# Discovery Server Route:
+spring.cloud.gateway.routes[1].id=discovery-server
+spring.cloud.gateway.routes[1].uri=http://localhost:8761
+spring.cloud.gateway.routes[1].predicates[0]=Path=/eureka/web
+spring.cloud.gateway.routes[1].filters[0]=SetPath=/
+
+# Discovery Server Static Resources Route:
+spring.cloud.gateway.routes[2].id=discovery-server
+spring.cloud.gateway.routes[2].uri=http://localhost:8761
+spring.cloud.gateway.routes[2].predicates[0]=Path=/eureka/**
+
+# Logging Service Route:
+spring.cloud.gateway.routes[3].id=logging-service
+spring.cloud.gateway.routes[3].uri=lb://logging-service
+spring.cloud.gateway.routes[3].predicates[0]=Path=/api/admin/logging-service/**
+
+# User Service Route:
+spring.cloud.gateway.routes[4].id=user-service
+spring.cloud.gateway.routes[4].uri=lb://user-service
+spring.cloud.gateway.routes[4].predicates[0]=Path=/api/admin/user-service/**
diff --git a/api-gateway/target/classes/banner.txt b/api-gateway/target/classes/banner.txt
new file mode 100644
index 0000000..12e81a1
--- /dev/null
+++ b/api-gateway/target/classes/banner.txt
@@ -0,0 +1,16 @@
+${AnsiColor.BLUE}
+ ███████████ █████ ███████████ █████ ████
+░░███░░░░░███ ░░███ ░░███░░░░░███ ░░███ ░░███
+ ░███ ░███ ██████ █████ ███████ ░███ ░███ ████████ ██████ ███████ ██████ ██████ ██████ ░███
+ ░██████████ ███░░███ ███░░ ░░░███░ ░██████████ ░░███░░███ ███░░███░░░███░ ███░░███ ███░░███ ███░░███ ░███
+ ░███░░░░░███░███████ ░░█████ ░███ ░███░░░░░░ ░███ ░░░ ░███ ░███ ░███ ░███ ░███░███ ░░░ ░███ ░███ ░███
+ ░███ ░███░███░░░ ░░░░███ ░███ ███ ░███ ░███ ░███ ░███ ░███ ███░███ ░███░███ ███░███ ░███ ░███
+ ███████████ ░░██████ ██████ ░░█████ █████ █████ ░░██████ ░░█████ ░░██████ ░░██████ ░░██████ █████
+░░░░░░░░░░░ ░░░░░░ ░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░░ ░░░░░░ ░░░░░░ ░░░░░
+
+
+
+${spring-boot.formatted-version}
+${application.title}
+${application.version}
+
diff --git a/api-gateway/target/maven-archiver/pom.properties b/api-gateway/target/maven-archiver/pom.properties
new file mode 100644
index 0000000..319e55d
--- /dev/null
+++ b/api-gateway/target/maven-archiver/pom.properties
@@ -0,0 +1,3 @@
+artifactId=api-gateway
+groupId=com.csaba79coder
+version=3.0.2
diff --git a/api-gateway/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/api-gateway/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
new file mode 100644
index 0000000..15f87ca
--- /dev/null
+++ b/api-gateway/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
@@ -0,0 +1 @@
+com\csaba79coder\apigateway\ApiGatewayApplication.class
diff --git a/api-gateway/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/api-gateway/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
new file mode 100644
index 0000000..5a76377
--- /dev/null
+++ b/api-gateway/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
@@ -0,0 +1 @@
+C:\Users\Computer\Documents\Java\Progmatic\best-protocol\api-gateway\src\main\java\com\csaba79coder\apigateway\ApiGatewayApplication.java
diff --git a/discovery-server/target/discovery-server-3.0.2.jar b/discovery-server/target/discovery-server-3.0.2.jar
index 9c84ce3..d74415d 100644
Binary files a/discovery-server/target/discovery-server-3.0.2.jar and b/discovery-server/target/discovery-server-3.0.2.jar differ
diff --git a/doc/developer-documentation/DeveloperDocumentationBestProtocol.docx b/doc/developer-documentation/DeveloperDocumentationBestProtocol.docx
new file mode 100644
index 0000000..2d56261
Binary files /dev/null and b/doc/developer-documentation/DeveloperDocumentationBestProtocol.docx differ
diff --git a/doc/pad/BestProtocol PAD 1.0.docx b/doc/pad/BestProtocol PAD 1.0.docx
new file mode 100644
index 0000000..bb75f17
Binary files /dev/null and b/doc/pad/BestProtocol PAD 1.0.docx differ
diff --git a/doc/pad/BestProtocol PAD 2.0.docx b/doc/pad/BestProtocol PAD 2.0.docx
new file mode 100644
index 0000000..30b270c
Binary files /dev/null and b/doc/pad/BestProtocol PAD 2.0.docx differ
diff --git a/doc/pad/gantt-diagram/Best Protocol- Gannt diagram.xlsx b/doc/pad/gantt-diagram/Best Protocol- Gannt diagram.xlsx
new file mode 100644
index 0000000..ec825e9
Binary files /dev/null and b/doc/pad/gantt-diagram/Best Protocol- Gannt diagram.xlsx differ
diff --git a/doc/presentation-final/BestProtocolPresentationFinal05052023.pptx b/doc/presentation-final/BestProtocolPresentationFinal05052023.pptx
new file mode 100644
index 0000000..d610ceb
Binary files /dev/null and b/doc/presentation-final/BestProtocolPresentationFinal05052023.pptx differ
diff --git a/doc/presentation/BestProtocolPresentation21042023.pptx b/doc/presentation/BestProtocolPresentation21042023.pptx
new file mode 100644
index 0000000..2d47841
Binary files /dev/null and b/doc/presentation/BestProtocolPresentation21042023.pptx differ
diff --git "a/doc/requirement/Ko\314\210vetelme\314\201nyek_Szoftverfejlesztes.pdf" "b/doc/requirement/Ko\314\210vetelme\314\201nyek_Szoftverfejlesztes.pdf"
new file mode 100644
index 0000000..cc21721
Binary files /dev/null and "b/doc/requirement/Ko\314\210vetelme\314\201nyek_Szoftverfejlesztes.pdf" differ
diff --git a/doc/requirement/PAD_v01.docx b/doc/requirement/PAD_v01.docx
new file mode 100644
index 0000000..50714cc
Binary files /dev/null and b/doc/requirement/PAD_v01.docx differ
diff --git a/doc/requirement/Szoftverfejlesztes_Minta_feladat.docx b/doc/requirement/Szoftverfejlesztes_Minta_feladat.docx
new file mode 100644
index 0000000..0b462b3
Binary files /dev/null and b/doc/requirement/Szoftverfejlesztes_Minta_feladat.docx differ
diff --git a/doc/user-documentation/UserDocumentationBestProtocol.docx b/doc/user-documentation/UserDocumentationBestProtocol.docx
new file mode 100644
index 0000000..f7efcfb
Binary files /dev/null and b/doc/user-documentation/UserDocumentationBestProtocol.docx differ
diff --git a/frontend/dist/frontend/3rdpartylicenses.txt b/frontend/dist/frontend/3rdpartylicenses.txt
new file mode 100644
index 0000000..6b0b8f6
--- /dev/null
+++ b/frontend/dist/frontend/3rdpartylicenses.txt
@@ -0,0 +1,454 @@
+@angular/common
+MIT
+
+@angular/core
+MIT
+
+@angular/forms
+MIT
+
+@angular/platform-browser
+MIT
+
+@angular/router
+MIT
+
+@fortawesome/fontawesome-free
+(CC-BY-4.0 AND OFL-1.1 AND MIT)
+Fonticons, Inc. (https://fontawesome.com)
+
+--------------------------------------------------------------------------------
+
+Font Awesome Free License
+
+Font Awesome Free is free, open source, and GPL friendly. You can use it for
+commercial projects, open source projects, or really almost whatever you want.
+Full Font Awesome Free license: https://fontawesome.com/license/free.
+
+--------------------------------------------------------------------------------
+
+# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
+
+The Font Awesome Free download is licensed under a Creative Commons
+Attribution 4.0 International License and applies to all icons packaged
+as SVG and JS file types.
+
+--------------------------------------------------------------------------------
+
+# Fonts: SIL OFL 1.1 License
+
+In the Font Awesome Free download, the SIL OFL license applies to all icons
+packaged as web and desktop font files.
+
+Copyright (c) 2023 Fonticons, Inc. (https://fontawesome.com)
+with Reserved Font Name: "Font Awesome".
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+SIL OPEN FONT LICENSE
+Version 1.1 - 26 February 2007
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting — in part or in whole — any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
+
+--------------------------------------------------------------------------------
+
+# Code: MIT License (https://opensource.org/licenses/MIT)
+
+In the Font Awesome Free download, the MIT license applies to all non-font and
+non-icon files.
+
+Copyright 2023 Fonticons, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in the
+Software without restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+--------------------------------------------------------------------------------
+
+# Attribution
+
+Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
+Awesome Free files already contain embedded comments with sufficient
+attribution, so you shouldn't need to do anything additional when using these
+files normally.
+
+We've kept attribution comments terse, so we ask that you do not actively work
+to remove them from files, especially code. They're a great way for folks to
+learn about Font Awesome.
+
+--------------------------------------------------------------------------------
+
+# Brand Icons
+
+All brand icons are trademarks of their respective owners. The use of these
+trademarks does not indicate endorsement of the trademark holder by Font
+Awesome, nor vice versa. **Please do not use brand logos for any purpose except
+to represent the company, product, or service to which they refer.**
+
+
+bootstrap
+MIT
+The MIT License (MIT)
+
+Copyright (c) 2011-2022 Twitter, Inc.
+Copyright (c) 2011-2022 The Bootstrap Authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+rxjs
+Apache-2.0
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
+tslib
+0BSD
+Copyright (c) Microsoft Corporation.
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+zone.js
+MIT
+The MIT License
+
+Copyright (c) 2010-2022 Google LLC. https://angular.io/license
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/frontend/dist/frontend/fa-brands-400.672e913fb6f186fb.woff2 b/frontend/dist/frontend/fa-brands-400.672e913fb6f186fb.woff2
new file mode 100644
index 0000000..5929101
Binary files /dev/null and b/frontend/dist/frontend/fa-brands-400.672e913fb6f186fb.woff2 differ
diff --git a/frontend/dist/frontend/fa-brands-400.d67d1219bc30e68d.ttf b/frontend/dist/frontend/fa-brands-400.d67d1219bc30e68d.ttf
new file mode 100644
index 0000000..641a489
Binary files /dev/null and b/frontend/dist/frontend/fa-brands-400.d67d1219bc30e68d.ttf differ
diff --git a/frontend/dist/frontend/fa-regular-400.073940ed482e0363.ttf b/frontend/dist/frontend/fa-regular-400.073940ed482e0363.ttf
new file mode 100644
index 0000000..7d634a2
Binary files /dev/null and b/frontend/dist/frontend/fa-regular-400.073940ed482e0363.ttf differ
diff --git a/frontend/dist/frontend/fa-regular-400.c7c7429f84794f4d.woff2 b/frontend/dist/frontend/fa-regular-400.c7c7429f84794f4d.woff2
new file mode 100644
index 0000000..953d554
Binary files /dev/null and b/frontend/dist/frontend/fa-regular-400.c7c7429f84794f4d.woff2 differ
diff --git a/frontend/dist/frontend/fa-solid-900.c9f6e41813989c48.ttf b/frontend/dist/frontend/fa-solid-900.c9f6e41813989c48.ttf
new file mode 100644
index 0000000..b3a2b64
Binary files /dev/null and b/frontend/dist/frontend/fa-solid-900.c9f6e41813989c48.ttf differ
diff --git a/frontend/dist/frontend/fa-solid-900.d5d28b783e396c28.woff2 b/frontend/dist/frontend/fa-solid-900.d5d28b783e396c28.woff2
new file mode 100644
index 0000000..83433f4
Binary files /dev/null and b/frontend/dist/frontend/fa-solid-900.d5d28b783e396c28.woff2 differ
diff --git a/frontend/dist/frontend/fa-v4compatibility.200e567a093eee3f.ttf b/frontend/dist/frontend/fa-v4compatibility.200e567a093eee3f.ttf
new file mode 100644
index 0000000..e4eea68
Binary files /dev/null and b/frontend/dist/frontend/fa-v4compatibility.200e567a093eee3f.ttf differ
diff --git a/frontend/dist/frontend/fa-v4compatibility.e993637ad6526525.woff2 b/frontend/dist/frontend/fa-v4compatibility.e993637ad6526525.woff2
new file mode 100644
index 0000000..e804f18
Binary files /dev/null and b/frontend/dist/frontend/fa-v4compatibility.e993637ad6526525.woff2 differ
diff --git a/frontend/dist/frontend/favicon.ico b/frontend/dist/frontend/favicon.ico
new file mode 100644
index 0000000..eedf292
Binary files /dev/null and b/frontend/dist/frontend/favicon.ico differ
diff --git a/frontend/dist/frontend/index.html b/frontend/dist/frontend/index.html
new file mode 100644
index 0000000..abe8b55
--- /dev/null
+++ b/frontend/dist/frontend/index.html
@@ -0,0 +1,15 @@
+
@WebMvcTest(UserController.class)
+@ContextConfiguration(classes=UserController.class)
+@ExtendWith(org.mockito.junit.jupiter.MockitoExtension.class)
+public class UserControllerIT
+extends Object
+
Integration test class for the UserController.
+ This class tests the behavior of the UserController by sending mock HTTP requests
+ and asserting the responses returned by the controller methods.
This method tests the behavior of the renderAllUsers() method of the UserController.
+ It mocks the UserService to return a list of dummy users and then performs a GET request to the UserController endpoint.
+ It asserts that the response status is 200 OK and the response body contains the expected list of users.
This method tests the behavior of the addNewUser() method of the UserController.
+ It mocks the UserService to return a dummy user and then performs a POST request to the UserController endpoint.
+ It asserts that the response status is 201 CREATED and the response body contains the expected user.
This method tests the behavior of the renderUserById() method of the UserController.
+ It mocks the UserService to return a dummy user and then performs a GET request to the UserController endpoint.
+ It asserts that the response status is 200 OK and the response body contains the expected user.
This method tests the behavior of the modifyUserById() method of the UserController.
+ It mocks the UserService to return a dummy user and then performs a PUT request to the UserController endpoint.
+ It asserts that the response status is 200 OK and the response body contains the expected user.
@SpringBootApplication
+@EnableDiscoveryClient
+public class UserServiceApplication
+extends Object
+
This is the main class for the User Service application. It is
+ annotated with @SpringBootApplication, which is a convenience
+ annotation that combines three other annotations:
+ - @Configuration: indicates that this class provides configuration
+ information for the application context.
+ - @EnableAutoConfiguration: enables Spring Boot's autoconfiguration
+ feature, which automatically configures the application based on
+ classpath settings, other beans, and various property settings.
+ - @ComponentScan: tells Spring to scan this package and its
+ sub-packages for components (i.e., beans) that can be autowired
+ into other beans.
+
+ Additionally, this class is annotated with @EnableDiscoveryClient,
+ which enables it to register with a service registry (such as Eureka)
+ as a discovery client. This allows other services to discover and
+ communicate with this service.
@MappedSuperclass
+public class Auditable
+extends Identifier
+
A base class for entities that need to be audited. This class provides the common audit fields such as created_at,
+ updated_at, created_by and updated_by.
+
+ This class is annotated with @MappedSuperclass which means that the properties of this class will be mapped to the
+ properties of its subclasses.
@MappedSuperclass
+public class Identifier
+extends Object
+
A base class for entities that have an ID field. This class provides a unique ID field which is generated using the
+ UUID strategy.
+
+ This class is annotated with @MappedSuperclass which means that the properties of this class will be mapped to the
+ properties of its subclasses.
This field represents the email address of this user.
+ It is annotated with @Column(name = "email", unique = true), which tells Hibernate to use the "email" column
+ in the database to store the value of this field. It is also annotated with @Column(unique = true), which
+ tells Hibernate to add a unique constraint to the "email" column. This means that the value of this field
+ must be unique in the database.
This field represents the password of this user.
+ It is annotated with @JsonProperty(access = JsonProperty.Access.WRITE_ONLY), which tells Jackson to only
+ serialize this field, but not deserialize it. This means that when a user is created or updated, the password
+ field will not be included in the request body. This is because we don't want to store the password in the
+ database as plain text. Instead, we will use the BCryptPasswordEncoder to encode the password before storing it
+ in the database, also the model has no such property as password, it is only used for validation.
This field represents the availability status of this user.
+ It is an enumeration value of type "Availability", which can be
+ one of several possible values: AVAILABLE, UNAVAILABLE, or DELETED.
This field represents the role of the user within the system. It is
+ annotated with @Enumerated(EnumType.STRING), which tells Hibernate
+ to store the value of the enum in the database as a string. It is
+ also annotated with @Column(name = "user_role"), which tells
+ Hibernate to use the "user_role" column in the database to store
+ this value. Finally, it is initialized with the default value of
+ Role.USER, which represents a regular user.
Adds a new user to the database if the email doesn't already exist,
+ and if the email and password meet certain validation criteria.
+ The new user is represented by a UserNewModel object.
+
+
Parameters:
+
userNewModel - the UserNewModel object representing the new user
+
Returns:
+
a UserModel object representing the newly added user
+
Throws:
+
IllegalArgumentException - if the email already exists, or if the
+ email or password do not meet the validation criteria
Returns the enum constant of this class with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this class. (Extraneous whitespace characters are
+not permitted.)
+
+
Parameters:
+
name - the name of the enum constant to be returned.
Returns the enum constant of this class with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this class. (Extraneous whitespace characters are
+not permitted.)
+
+
Parameters:
+
name - the name of the enum constant to be returned.
Validates an email address. Returns true if the email is valid,
+ and false otherwise.
+
+
Parameters:
+
email - the email address to validate
+
Returns:
+
true if the email is valid, false otherwise
+
+
+
+
+
+
isValidPassword
+
public staticbooleanisValidPassword(String password)
+
Validates a password. Returns true if the password is valid,
+ and false otherwise. A valid password must be at least 8 characters
+ long and contain at least one uppercase letter, one lowercase letter,
+ one digit, and one special character.
to the "/en/api/admin/governments/{id}" endpoint with the "en" language parameter and a government ID,
and verifying that the response contains a list of government models with the expected properties.
+
SUPER_ADMIN - Enum constant in enum class com.csaba79coder.userservice.model.value.Role
+
+
The SUPER_ADMIN role represents a super administrator of the
+ system, with the highest level of privileges.
Test case for verifying that the {@link ControllerExceptionHandler#//responseBodyWithMessage}
method correctly creates the response body with the given error code and message.
diff --git a/javadoc/package-search-index.js b/javadoc/package-search-index.js
index 7d77dc8..1ada895 100644
--- a/javadoc/package-search-index.js
+++ b/javadoc/package-search-index.js
@@ -1 +1 @@
-packageSearchIndex = [{"l":"All Packages","u":"allpackages-index.html"},{"l":"com.csaba79coder.apigateway"},{"l":"com.csaba79coder.bestprotocol"},{"l":"com.csaba79coder.bestprotocol.bootstrap"},{"l":"com.csaba79coder.bestprotocol.controller"},{"l":"com.csaba79coder.bestprotocol.controller.exception"},{"l":"com.csaba79coder.bestprotocol.controller.value"},{"l":"com.csaba79coder.bestprotocol.model.base.entity"},{"l":"com.csaba79coder.bestprotocol.model.government.entity"},{"l":"com.csaba79coder.bestprotocol.model.government.persistence"},{"l":"com.csaba79coder.bestprotocol.model.government.service"},{"l":"com.csaba79coder.bestprotocol.model.menu.entity"},{"l":"com.csaba79coder.bestprotocol.model.menu.persistence"},{"l":"com.csaba79coder.bestprotocol.model.menu.service"},{"l":"com.csaba79coder.bestprotocol.model.representative.entity"},{"l":"com.csaba79coder.bestprotocol.model.representative.persistence"},{"l":"com.csaba79coder.bestprotocol.model.representative.service"},{"l":"com.csaba79coder.bestprotocol.model.value"},{"l":"com.csaba79coder.bestprotocol.util"},{"l":"com.csaba79coder.bestprotocol.util.mapper"},{"l":"com.csaba79coder.discoveryserver"},{"l":"com.csaba79coder.loggingservice"},{"l":"com.csaba79coder.loggingservice.controller"},{"l":"com.csaba79coder.loggingservice.model.entity"},{"l":"com.csaba79coder.loggingservice.model.persistence"},{"l":"com.csaba79coder.loggingservice.model.service"},{"l":"com.csaba79coder.loggingservice.util"}];updateSearchResults();
\ No newline at end of file
+packageSearchIndex = [{"l":"All Packages","u":"allpackages-index.html"},{"l":"com.csaba79coder.apigateway"},{"l":"com.csaba79coder.bestprotocol"},{"l":"com.csaba79coder.bestprotocol.bootstrap"},{"l":"com.csaba79coder.bestprotocol.controller"},{"l":"com.csaba79coder.bestprotocol.controller.exception"},{"l":"com.csaba79coder.bestprotocol.controller.value"},{"l":"com.csaba79coder.bestprotocol.model.base.entity"},{"l":"com.csaba79coder.bestprotocol.model.government.entity"},{"l":"com.csaba79coder.bestprotocol.model.government.persistence"},{"l":"com.csaba79coder.bestprotocol.model.government.service"},{"l":"com.csaba79coder.bestprotocol.model.menu.entity"},{"l":"com.csaba79coder.bestprotocol.model.menu.persistence"},{"l":"com.csaba79coder.bestprotocol.model.menu.service"},{"l":"com.csaba79coder.bestprotocol.model.representative.entity"},{"l":"com.csaba79coder.bestprotocol.model.representative.persistence"},{"l":"com.csaba79coder.bestprotocol.model.representative.service"},{"l":"com.csaba79coder.bestprotocol.model.value"},{"l":"com.csaba79coder.bestprotocol.util"},{"l":"com.csaba79coder.bestprotocol.util.mapper"},{"l":"com.csaba79coder.discoveryserver"},{"l":"com.csaba79coder.loggingservice"},{"l":"com.csaba79coder.loggingservice.controller"},{"l":"com.csaba79coder.loggingservice.model.entity"},{"l":"com.csaba79coder.loggingservice.model.persistence"},{"l":"com.csaba79coder.loggingservice.model.service"},{"l":"com.csaba79coder.loggingservice.util"},{"l":"com.csaba79coder.userservice"},{"l":"com.csaba79coder.userservice.controller"},{"l":"com.csaba79coder.userservice.model.base.entity"},{"l":"com.csaba79coder.userservice.model.entity"},{"l":"com.csaba79coder.userservice.model.persistence"},{"l":"com.csaba79coder.userservice.model.service"},{"l":"com.csaba79coder.userservice.model.value"},{"l":"com.csaba79coder.userservice.util"}];updateSearchResults();
\ No newline at end of file
diff --git a/javadoc/type-search-index.js b/javadoc/type-search-index.js
index eb690a6..28b4535 100644
--- a/javadoc/type-search-index.js
+++ b/javadoc/type-search-index.js
@@ -1 +1 @@
-typeSearchIndex = [{"l":"All Classes and Interfaces","u":"allclasses-index.html"},{"p":"com.csaba79coder.apigateway","l":"ApiGatewayApplication"},{"p":"com.csaba79coder.bestprotocol.model.base.entity","l":"Auditable"},{"p":"com.csaba79coder.bestprotocol.model.value","l":"Availability"},{"p":"com.csaba79coder.bestprotocol","l":"BestProtocolApplication"},{"p":"com.csaba79coder.bestprotocol","l":"BestProtocolApplicationTests"},{"p":"com.csaba79coder.bestprotocol.controller.exception","l":"ControllerExceptionHandler"},{"p":"com.csaba79coder.bestprotocol.controller.exception","l":"ControllerExceptionHandlerTest"},{"p":"com.csaba79coder.bestprotocol.bootstrap","l":"DataLoader"},{"p":"com.csaba79coder.discoveryserver","l":"DiscoveryServerApplication"},{"p":"com.csaba79coder.bestprotocol.controller.value","l":"ErrorCode"},{"p":"com.csaba79coder.bestprotocol.model.government.entity","l":"Government"},{"p":"com.csaba79coder.bestprotocol.controller","l":"GovernmentController"},{"p":"com.csaba79coder.bestprotocol","l":"GovernmentControllerIT"},{"p":"com.csaba79coder.bestprotocol.model.government.persistence","l":"GovernmentRepository"},{"p":"com.csaba79coder.bestprotocol.model.government.service","l":"GovernmentService"},{"p":"com.csaba79coder.bestprotocol.model.government.entity","l":"GovernmentTranslation"},{"p":"com.csaba79coder.bestprotocol.model.government.persistence","l":"GovernmentTranslationRepository"},{"p":"com.csaba79coder.bestprotocol.model.base.entity","l":"Identifier"},{"p":"com.csaba79coder.bestprotocol.model.base.entity","l":"IdentifierLong"},{"p":"com.csaba79coder.bestprotocol.util","l":"ImageUtil"},{"p":"com.csaba79coder.bestprotocol.util","l":"ImageUtilTest"},{"p":"com.csaba79coder.bestprotocol.controller","l":"IndexController"},{"p":"com.csaba79coder.loggingservice.model.entity","l":"Logging"},{"p":"com.csaba79coder.loggingservice.controller","l":"LoggingController"},{"p":"com.csaba79coder.loggingservice.model.persistence","l":"LoggingRepository"},{"p":"com.csaba79coder.loggingservice.model.service","l":"LoggingService"},{"p":"com.csaba79coder.loggingservice","l":"LoggingServiceApplication"},{"p":"com.csaba79coder.bestprotocol.util.mapper","l":"Mapper"},{"p":"com.csaba79coder.loggingservice.util","l":"Mapper"},{"p":"com.csaba79coder.bestprotocol.util.mapper","l":"MapperTest"},{"p":"com.csaba79coder.bestprotocol.model.menu.entity","l":"MenuTranslation"},{"p":"com.csaba79coder.bestprotocol.controller","l":"MenuTranslationController"},{"p":"com.csaba79coder.bestprotocol","l":"MenuTranslationControllerIT"},{"p":"com.csaba79coder.bestprotocol.model.menu.persistence","l":"MenuTranslationRepository"},{"p":"com.csaba79coder.bestprotocol.model.menu.service","l":"MenuTranslationService"},{"p":"com.csaba79coder.bestprotocol.model.government.entity","l":"PreviousJobTitleTranslation"},{"p":"com.csaba79coder.bestprotocol.model.government.persistence","l":"PreviousJobTitleTranslationRepository"},{"p":"com.csaba79coder.bestprotocol.model.representative.entity","l":"Representative"},{"p":"com.csaba79coder.bestprotocol.controller","l":"RepresentativeController"},{"p":"com.csaba79coder.bestprotocol","l":"RepresentativeControllerIT"},{"p":"com.csaba79coder.bestprotocol.model.representative.persistence","l":"RepresentativeRepository"},{"p":"com.csaba79coder.bestprotocol.model.representative.service","l":"RepresentativeService"},{"p":"com.csaba79coder.bestprotocol.model.representative.entity","l":"RepresentativeTranslation"},{"p":"com.csaba79coder.bestprotocol.model.representative.persistence","l":"RepresentativeTranslationRepository"}];updateSearchResults();
\ No newline at end of file
+typeSearchIndex = [{"l":"All Classes and Interfaces","u":"allclasses-index.html"},{"p":"com.csaba79coder.apigateway","l":"ApiGatewayApplication"},{"p":"com.csaba79coder.bestprotocol.model.base.entity","l":"Auditable"},{"p":"com.csaba79coder.userservice.model.base.entity","l":"Auditable"},{"p":"com.csaba79coder.bestprotocol.model.value","l":"Availability"},{"p":"com.csaba79coder.userservice.model.value","l":"Availability"},{"p":"com.csaba79coder.bestprotocol","l":"BestProtocolApplication"},{"p":"com.csaba79coder.bestprotocol","l":"BestProtocolApplicationTests"},{"p":"com.csaba79coder.bestprotocol.controller.exception","l":"ControllerExceptionHandler"},{"p":"com.csaba79coder.bestprotocol.controller.exception","l":"ControllerExceptionHandlerTest"},{"p":"com.csaba79coder.bestprotocol.bootstrap","l":"DataLoader"},{"p":"com.csaba79coder.discoveryserver","l":"DiscoveryServerApplication"},{"p":"com.csaba79coder.bestprotocol.controller.value","l":"ErrorCode"},{"p":"com.csaba79coder.bestprotocol.model.government.entity","l":"Government"},{"p":"com.csaba79coder.bestprotocol.controller","l":"GovernmentController"},{"p":"com.csaba79coder.bestprotocol","l":"GovernmentControllerIT"},{"p":"com.csaba79coder.bestprotocol.model.government.persistence","l":"GovernmentRepository"},{"p":"com.csaba79coder.bestprotocol.model.government.service","l":"GovernmentService"},{"p":"com.csaba79coder.bestprotocol.model.government.entity","l":"GovernmentTranslation"},{"p":"com.csaba79coder.bestprotocol.model.government.persistence","l":"GovernmentTranslationRepository"},{"p":"com.csaba79coder.bestprotocol.model.base.entity","l":"Identifier"},{"p":"com.csaba79coder.userservice.model.base.entity","l":"Identifier"},{"p":"com.csaba79coder.bestprotocol.model.base.entity","l":"IdentifierLong"},{"p":"com.csaba79coder.bestprotocol.util","l":"ImageUtil"},{"p":"com.csaba79coder.bestprotocol.util","l":"ImageUtilTest"},{"p":"com.csaba79coder.bestprotocol.controller","l":"IndexController"},{"p":"com.csaba79coder.loggingservice.model.entity","l":"Logging"},{"p":"com.csaba79coder.loggingservice.controller","l":"LoggingController"},{"p":"com.csaba79coder.loggingservice.model.persistence","l":"LoggingRepository"},{"p":"com.csaba79coder.loggingservice.model.service","l":"LoggingService"},{"p":"com.csaba79coder.loggingservice","l":"LoggingServiceApplication"},{"p":"com.csaba79coder.bestprotocol.util.mapper","l":"Mapper"},{"p":"com.csaba79coder.loggingservice.util","l":"Mapper"},{"p":"com.csaba79coder.userservice.util","l":"Mapper"},{"p":"com.csaba79coder.bestprotocol.util.mapper","l":"MapperTest"},{"p":"com.csaba79coder.userservice.util","l":"MapperTest"},{"p":"com.csaba79coder.bestprotocol.model.menu.entity","l":"MenuTranslation"},{"p":"com.csaba79coder.bestprotocol.controller","l":"MenuTranslationController"},{"p":"com.csaba79coder.bestprotocol","l":"MenuTranslationControllerIT"},{"p":"com.csaba79coder.bestprotocol.model.menu.persistence","l":"MenuTranslationRepository"},{"p":"com.csaba79coder.bestprotocol.model.menu.service","l":"MenuTranslationService"},{"p":"com.csaba79coder.bestprotocol.model.government.entity","l":"PreviousJobTitleTranslation"},{"p":"com.csaba79coder.bestprotocol.model.government.persistence","l":"PreviousJobTitleTranslationRepository"},{"p":"com.csaba79coder.bestprotocol.model.representative.entity","l":"Representative"},{"p":"com.csaba79coder.bestprotocol.controller","l":"RepresentativeController"},{"p":"com.csaba79coder.bestprotocol","l":"RepresentativeControllerIT"},{"p":"com.csaba79coder.bestprotocol.model.representative.persistence","l":"RepresentativeRepository"},{"p":"com.csaba79coder.bestprotocol.model.representative.service","l":"RepresentativeService"},{"p":"com.csaba79coder.bestprotocol.model.representative.entity","l":"RepresentativeTranslation"},{"p":"com.csaba79coder.bestprotocol.model.representative.persistence","l":"RepresentativeTranslationRepository"},{"p":"com.csaba79coder.userservice.model.value","l":"Role"},{"p":"com.csaba79coder.userservice.model.entity","l":"User"},{"p":"com.csaba79coder.userservice.controller","l":"UserController"},{"p":"com.csaba79coder.userservice","l":"UserControllerIT"},{"p":"com.csaba79coder.userservice.model.persistence","l":"UserRepository"},{"p":"com.csaba79coder.userservice.model.service","l":"UserService"},{"p":"com.csaba79coder.userservice","l":"UserServiceApplication"},{"p":"com.csaba79coder.userservice.util","l":"ValidationUtil"},{"p":"com.csaba79coder.userservice.util","l":"ValidationUtilTest"}];updateSearchResults();
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index e57c883..0567ca6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,6 +21,7 @@
discovery-serverapi-gatewaylogging-service
+ user-service
@@ -29,7 +30,7 @@
17UTF-8UTF-8
- 3.0.0-SNAPSHOT
+ 3.2.0-SNAPSHOT2022.0.2
diff --git a/postman/best-protocol.postman_collection.json b/postman/best-protocol.postman_collection.json
new file mode 100644
index 0000000..dfa5951
--- /dev/null
+++ b/postman/best-protocol.postman_collection.json
@@ -0,0 +1,656 @@
+{
+ "info": {
+ "_postman_id": "db36b327-2969-4f1f-b3d8-cc2b2820328f",
+ "name": "best-protocol",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
+ "_exporter_id": "18091890"
+ },
+ "item": [
+ {
+ "name": "Render All Representatives",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8080/hu/api/admin/gov-representatives",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "hu",
+ "api",
+ "admin",
+ "gov-representatives"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Render All Representatives By Search",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8080/hu/api/admin/gov-representatives",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "hu",
+ "api",
+ "admin",
+ "gov-representatives"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Render Representatives By GovernmentId",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8080/hu/api/admin/gov-representatives/governments/1",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "hu",
+ "api",
+ "admin",
+ "gov-representatives",
+ "governments",
+ "1"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Render Representatives By GovernmentId Id And Search",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8080/hu/api/admin/gov-representatives/governments/9",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "hu",
+ "api",
+ "admin",
+ "gov-representatives",
+ "governments",
+ "9"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Render All Governments By Language",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8080/hu/api/admin/governments",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "hu",
+ "api",
+ "admin",
+ "governments"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Render All Governments By Hebrew Language",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8080/hu/api/admin/governments",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "hu",
+ "api",
+ "admin",
+ "governments"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Render All Governments By Hungarian",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8080/hu/api/admin/governments",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "hu",
+ "api",
+ "admin",
+ "governments"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Add New Representative",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "name",
+ "value": "Csaba Vadasz",
+ "type": "text"
+ },
+ {
+ "key": "government",
+ "value": "Miniszterelnökség",
+ "type": "text"
+ },
+ {
+ "key": "secretairat",
+ "value": "Miniszterelnöki Titkárság",
+ "type": "text"
+ },
+ {
+ "key": "jobTitle",
+ "value": "Miniszterelnökséget vezető miniszter",
+ "type": "text"
+ },
+ {
+ "key": "phoneNumber",
+ "value": "+36-30-235-6304",
+ "type": "text"
+ },
+ {
+ "key": "email",
+ "value": "csabavadasz79@gmail.com",
+ "type": "text"
+ },
+ {
+ "key": "file",
+ "type": "file",
+ "src": "/C:/Users/Computer/Documents/Java/Progmatic/best-protocol/government-service/src/main/resources/static/images/placeholder.png"
+ },
+ {
+ "key": "note",
+ "value": "Ételérzékenység: nincs",
+ "type": "text"
+ },
+ {
+ "key": "address",
+ "value": "2011 Budakalász, Gerinc utca hrsz. 2287/3",
+ "type": "text"
+ }
+ ]
+ },
+ "url": {
+ "raw": "http://localhost:8080/hu/api/admin/gov-representatives",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "hu",
+ "api",
+ "admin",
+ "gov-representatives"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Add New Representative Not Hungarian",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "name",
+ "value": "Jichák Hercog",
+ "type": "text"
+ },
+ {
+ "key": "government",
+ "value": "Miniszterelnökség",
+ "type": "text"
+ },
+ {
+ "key": "secretairat",
+ "value": "N/A",
+ "type": "text"
+ },
+ {
+ "key": "jobTitle",
+ "value": "Miniszterelnök",
+ "type": "text"
+ },
+ {
+ "key": "phoneNumber",
+ "value": "+44-......",
+ "type": "text"
+ },
+ {
+ "key": "email",
+ "value": "secret@secret.com",
+ "type": "text"
+ },
+ {
+ "key": "file",
+ "type": "file",
+ "src": "/C:/Users/Computer/Desktop/JichákHercog.png"
+ },
+ {
+ "key": "note",
+ "value": "Ételérzékenység: nem eszik disznó húst",
+ "type": "text"
+ },
+ {
+ "key": "address",
+ "value": "91096 Jeruzsálem, Secret street 0",
+ "type": "text"
+ },
+ {
+ "key": "lang",
+ "value": "il",
+ "type": "text"
+ }
+ ]
+ },
+ "url": {
+ "raw": "http://localhost:8080/hu/api/admin/gov-representatives",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "hu",
+ "api",
+ "admin",
+ "gov-representatives"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Add New Representative Without Image",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "name",
+ "value": "Elek Teszt",
+ "type": "text"
+ },
+ {
+ "key": "jobTitle",
+ "value": "Pénzügyminiszter",
+ "type": "text"
+ },
+ {
+ "key": "address",
+ "value": "1077 Budapest, Rózsa utca 40/A",
+ "type": "text"
+ },
+ {
+ "key": "phoneNumber",
+ "value": "+36-30-111-11111",
+ "type": "text"
+ },
+ {
+ "key": "email",
+ "value": "tesztelek@teszt.com",
+ "type": "text"
+ },
+ {
+ "key": "file",
+ "type": "file",
+ "src": []
+ },
+ {
+ "key": "note",
+ "value": "Ételérzékenység: tejallergia",
+ "type": "text"
+ },
+ {
+ "key": "government",
+ "value": "Pénzügyminisztérium",
+ "type": "text"
+ },
+ {
+ "key": "secretairat",
+ "value": "Pénzügyi Titkárság",
+ "type": "text"
+ }
+ ]
+ },
+ "url": {
+ "raw": "http://localhost:8080/api/admin/gov-representatives",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "api",
+ "admin",
+ "gov-representatives"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Render All Menu",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8080/hu/api/admin/menu?translationKey=all",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "hu",
+ "api",
+ "admin",
+ "menu"
+ ],
+ "query": [
+ {
+ "key": "translationKey",
+ "value": "all"
+ },
+ {
+ "key": "",
+ "value": "",
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Add new log to log-service",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"message\": \"test log from postman with hour and min right format\",\r\n \"level\": \"info\",\r\n \"serviceName\": \"logging-service\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8080/api/admin/logging-service/log",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "api",
+ "admin",
+ "logging-service",
+ "log"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Render all logs",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8080/api/admin/logging-service/log",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "api",
+ "admin",
+ "logging-service",
+ "log"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Render All Users",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8080/api/admin/user-service/users",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "api",
+ "admin",
+ "user-service",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Add new User",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"username\": \"Csaba Vadasz\",\r\n \"email\": \"csabavadasz79@gmail.com\",\r\n \"password\": \"Almafa1234?\",\r\n \"repeatPassword\": \"Almafa1234?\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8080/api/admin/user-service/users",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "api",
+ "admin",
+ "user-service",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Add new User For Modify",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"username\": \"Teszt Elek\",\r\n \"email\": \"milton-teszt@gmail.com\",\r\n \"password\": \"Almafa1234?\",\r\n \"repeatPassword\": \"Almafa1234?\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8080/api/admin/user-service/users",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "api",
+ "admin",
+ "user-service",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Add new User With Existing Email",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"username\": \"Csaba Vadasz\",\r\n \"email\": \"csabavadasz79@gmail.com\",\r\n \"password\": \"Almafa1234?\",\r\n \"repeatPassword\": \"Almafa1234?\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8080/api/admin/user-service/users",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "api",
+ "admin",
+ "user-service",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Find User by Id",
+ "protocolProfileBehavior": {
+ "disableBodyPruning": true
+ },
+ "request": {
+ "method": "GET",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"username\": \"Csaba Vadasz\",\r\n \"email\": \"csabavadasz79@gmail.com\",\r\n \"password\": \"Almafa1234?\",\r\n \"repeatPassword\": \"Almafa1234?\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8080/api/admin/user-service/users/641ec24d-5256-4405-81f8-1c6bf06945f5",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "api",
+ "admin",
+ "user-service",
+ "users",
+ "641ec24d-5256-4405-81f8-1c6bf06945f5"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Modify An Existing User",
+ "request": {
+ "method": "PUT",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"role\": \"ADMIN\",\r\n \"availability\": \"ARCHIVE\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8080/api/admin/user-service/users/55722a26-757d-4280-8173-8dc5d9222d55",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8080",
+ "path": [
+ "api",
+ "admin",
+ "user-service",
+ "users",
+ "55722a26-757d-4280-8173-8dc5d9222d55"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/user-service/pom.xml b/user-service/pom.xml
new file mode 100644
index 0000000..96d3021
--- /dev/null
+++ b/user-service/pom.xml
@@ -0,0 +1,96 @@
+
+
+ 4.0.0
+
+ com.csaba79coder
+ best-protocol
+ 3.0.2
+
+
+ user-service
+
+
+ 17
+ 17
+ 17
+ UTF-8
+ UTF-8
+ 3.0.0-SNAPSHOT
+ 2022.0.2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.yaml
+ snakeyaml
+ 2.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+
+ com.mysql
+ mysql-connector-j
+ runtime
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.modelmapper
+ modelmapper
+ 3.1.1
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ com.h2database
+ h2
+ test
+
+
+ com.csaba79coder
+ api-contract
+ 3.0.2
+ compile
+
+
+ org.glassfish.hk2.external
+ aopalliance-repackaged
+ 3.0.3
+ test
+
+
+
+
\ No newline at end of file
diff --git a/user-service/src/main/java/com/csaba79coder/userservice/UserServiceApplication.java b/user-service/src/main/java/com/csaba79coder/userservice/UserServiceApplication.java
new file mode 100644
index 0000000..2db0808
--- /dev/null
+++ b/user-service/src/main/java/com/csaba79coder/userservice/UserServiceApplication.java
@@ -0,0 +1,39 @@
+package com.csaba79coder.userservice;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+
+/**
+ * This is the main class for the User Service application. It is
+ * annotated with @SpringBootApplication, which is a convenience
+ * annotation that combines three other annotations:
+ * - @Configuration: indicates that this class provides configuration
+ * information for the application context.
+ * - @EnableAutoConfiguration: enables Spring Boot's autoconfiguration
+ * feature, which automatically configures the application based on
+ * classpath settings, other beans, and various property settings.
+ * - @ComponentScan: tells Spring to scan this package and its
+ * sub-packages for components (i.e., beans) that can be autowired
+ * into other beans.
+ *
+ * Additionally, this class is annotated with @EnableDiscoveryClient,
+ * which enables it to register with a service registry (such as Eureka)
+ * as a discovery client. This allows other services to discover and
+ * communicate with this service.
+ */
+@SpringBootApplication
+@EnableDiscoveryClient // added @EnableDiscoveryClient for this to work as a discovery client
+public class UserServiceApplication {
+
+ /**
+ * This is the main method for the User Service application. It
+ * starts the Spring application context and launches the
+ * application.
+ *
+ * @param args command-line arguments passed to the application
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(UserServiceApplication.class, args);
+ }
+}
diff --git a/user-service/src/main/java/com/csaba79coder/userservice/controller/UserController.java b/user-service/src/main/java/com/csaba79coder/userservice/controller/UserController.java
new file mode 100644
index 0000000..de2c843
--- /dev/null
+++ b/user-service/src/main/java/com/csaba79coder/userservice/controller/UserController.java
@@ -0,0 +1,62 @@
+package com.csaba79coder.userservice.controller;
+
+import com.csaba79coder.bestprotocol.api.UserApi;
+import com.csaba79coder.bestprotocol.model.UserModel;
+import com.csaba79coder.bestprotocol.model.UserModifyModel;
+import com.csaba79coder.bestprotocol.model.UserNewModel;
+import com.csaba79coder.userservice.model.service.UserService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * responsible for the endpoints
+ */
+@RestController
+@RequiredArgsConstructor
+public class UserController implements UserApi {
+
+ private final UserService userService;
+
+ /**
+ * responsible for the endpoints
+ * @return the user model
+ * @param body the user model
+ */
+ @Override
+ public ResponseEntity addNewUser(UserNewModel body) {
+ return ResponseEntity.status(201).body(userService.addNewUser(body));
+ }
+
+ /**
+ * responsible for the endpoints
+ * @param userId the user id
+ * @return the user modify model
+ */
+ @Override
+ public ResponseEntity modifyUserById(UUID userId, UserModel body) {
+ return ResponseEntity.status(200).body(userService.modifyUserById(userId, body));
+ }
+
+ /**
+ * responsible for the endpoints
+ * @return the user model
+ */
+ @Override
+ public ResponseEntity> renderAllUsers() {
+ return ResponseEntity.status(200).body(userService.renderAllUsers());
+ }
+
+ /**
+ * responsible for the endpoints
+ * @param userId
+ * @return the user model
+ */
+ @Override
+ public ResponseEntity renderUserById(UUID userId) {
+ return ResponseEntity.status(200).body(userService.renderUserById(userId));
+ }
+}
diff --git a/user-service/src/main/java/com/csaba79coder/userservice/model/base/entity/Auditable.java b/user-service/src/main/java/com/csaba79coder/userservice/model/base/entity/Auditable.java
new file mode 100644
index 0000000..4961516
--- /dev/null
+++ b/user-service/src/main/java/com/csaba79coder/userservice/model/base/entity/Auditable.java
@@ -0,0 +1,37 @@
+package com.csaba79coder.userservice.model.base.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.MappedSuperclass;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.CreationTimestamp;
+
+import java.time.LocalDateTime;
+import java.util.UUID;
+
+/**
+ * A base class for entities that need to be audited. This class provides the common audit fields such as created_at,
+ * updated_at, created_by and updated_by.
+ *
+ * This class is annotated with @MappedSuperclass which means that the properties of this class will be mapped to the
+ * properties of its subclasses.
+ */
+@MappedSuperclass
+@Getter
+@Setter
+public class Auditable extends Identifier {
+
+ @CreationTimestamp
+ @Column(name = "created_at", nullable = false)
+ private LocalDateTime createdAt = LocalDateTime.now();
+
+ @CreationTimestamp
+ @Column(name = "updated_at", nullable = false)
+ private LocalDateTime updatedAt = LocalDateTime.now();
+
+ @Column(name = "created_by")
+ private UUID createdBy = UUID.fromString("6772c9dc-a7be-4826-963a-e376074fd4e7");
+
+ @Column(name = "updated_by")
+ private UUID updatedBy = UUID.fromString("dbd58012-9ee7-47d5-8f87-9bbc91583009");
+}
diff --git a/user-service/src/main/java/com/csaba79coder/userservice/model/base/entity/Identifier.java b/user-service/src/main/java/com/csaba79coder/userservice/model/base/entity/Identifier.java
new file mode 100644
index 0000000..8da0185
--- /dev/null
+++ b/user-service/src/main/java/com/csaba79coder/userservice/model/base/entity/Identifier.java
@@ -0,0 +1,27 @@
+package com.csaba79coder.userservice.model.base.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.MappedSuperclass;
+import lombok.Getter;
+
+import java.util.UUID;
+
+/**
+ * A base class for entities that have an ID field. This class provides a unique ID field which is generated using the
+ * UUID strategy.
+ *
+ * This class is annotated with @MappedSuperclass which means that the properties of this class will be mapped to the
+ * properties of its subclasses.
+ */
+@MappedSuperclass
+@Getter
+public class Identifier {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.UUID)
+ @Column(name = "id", nullable = false)
+ private UUID id;
+}
diff --git a/user-service/src/main/java/com/csaba79coder/userservice/model/entity/User.java b/user-service/src/main/java/com/csaba79coder/userservice/model/entity/User.java
new file mode 100644
index 0000000..d7a8b2c
--- /dev/null
+++ b/user-service/src/main/java/com/csaba79coder/userservice/model/entity/User.java
@@ -0,0 +1,115 @@
+package com.csaba79coder.userservice.model.entity;
+
+import com.csaba79coder.userservice.model.base.entity.Auditable;
+import com.csaba79coder.userservice.model.value.Availability;
+import com.csaba79coder.userservice.model.value.Role;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.Table;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.hibernate.annotations.Where;
+
+/**
+ * This class represents a user entity that can be persisted to a database
+ * using JPA.
+ */
+@Entity
+/**
+ * This annotation specifies the name of the database table to which
+ * this entity is mapped. In this case, the table name is "bp_user".
+ */
+@Table(name = "bp_user")
+/**
+ * This annotation indicates that this class can be embedded in other
+ * entities, rather than being treated as a standalone entity itself.
+ */
+@Embeddable
+/**
+ * This annotation indicates that this class has getter methods generated
+ * for all non-static fields. Getter methods are used to retrieve the value
+ * of an object's properties.
+ */
+@Getter
+/**
+ * This annotation indicates that this class has setter methods generated
+ * for all non-final non-static fields. Setter methods are used to set the
+ * value of an object's properties.
+ */
+@Setter
+/**
+ * This annotation generates a no-argument constructor for this class.
+ * The constructor initializes all fields to their default values.
+ */
+@NoArgsConstructor
+/**
+ * This annotation generates a constructor for this class that takes
+ * arguments for all non-static fields. The constructor initializes each
+ * field with the corresponding argument.
+ */
+@AllArgsConstructor
+/**
+ * This annotation specifies a SQL WHERE clause that is added to all
+ * SELECT statements generated for this entity. In this case, the clause
+ * ensures that only users whose "availability" field is not equal to
+ * "DELETED" are returned.
+ */
+@Where(clause = "availability != 'DELETED'")
+public class User extends Auditable {
+
+ /**
+ * This field represents the username of this user.
+ */
+ @Column(name = "user_name")
+ private String username;
+
+ /**
+ * This field represents the email address of this user.
+ * It is annotated with @Column(name = "email", unique = true), which tells Hibernate to use the "email" column
+ * in the database to store the value of this field. It is also annotated with @Column(unique = true), which
+ * tells Hibernate to add a unique constraint to the "email" column. This means that the value of this field
+ * must be unique in the database.
+ */
+ @Column(name = "email", unique = true)
+ private String email;
+
+ /**
+ * This field represents the password of this user.
+ * It is annotated with @JsonProperty(access = JsonProperty.Access.WRITE_ONLY), which tells Jackson to only
+ * serialize this field, but not deserialize it. This means that when a user is created or updated, the password
+ * field will not be included in the request body. This is because we don't want to store the password in the
+ * database as plain text. Instead, we will use the BCryptPasswordEncoder to encode the password before storing it
+ * in the database, also the model has no such property as password, it is only used for validation.
+ */
+ @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
+ @Column(name = "password")
+ private String password;
+
+ /**
+ * This field represents the availability status of this user.
+ * It is an enumeration value of type "Availability", which can be
+ * one of several possible values: AVAILABLE, UNAVAILABLE, or DELETED.
+ */
+ @Enumerated(EnumType.STRING)
+ @Column(name = "availability")
+ private Availability availability = Availability.AVAILABLE;
+
+ /**
+ * This field represents the role of the user within the system. It is
+ * annotated with @Enumerated(EnumType.STRING), which tells Hibernate
+ * to store the value of the enum in the database as a string. It is
+ * also annotated with @Column(name = "user_role"), which tells
+ * Hibernate to use the "user_role" column in the database to store
+ * this value. Finally, it is initialized with the default value of
+ * Role.USER, which represents a regular user.
+ */
+ @Enumerated(EnumType.STRING)
+ @Column(name = "user_role")
+ private Role role = Role.USER;
+}
diff --git a/user-service/src/main/java/com/csaba79coder/userservice/model/persistence/UserRepository.java b/user-service/src/main/java/com/csaba79coder/userservice/model/persistence/UserRepository.java
new file mode 100644
index 0000000..5d49d7c
--- /dev/null
+++ b/user-service/src/main/java/com/csaba79coder/userservice/model/persistence/UserRepository.java
@@ -0,0 +1,18 @@
+package com.csaba79coder.userservice.model.persistence;
+
+import com.csaba79coder.userservice.model.entity.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * responsible for the persistence layer
+ */
+@Repository
+public interface UserRepository extends JpaRepository {
+
+ Optional findUserById(UUID id);
+ Optional findUserByEmail(String email);
+}
diff --git a/user-service/src/main/java/com/csaba79coder/userservice/model/service/UserService.java b/user-service/src/main/java/com/csaba79coder/userservice/model/service/UserService.java
new file mode 100644
index 0000000..34aa452
--- /dev/null
+++ b/user-service/src/main/java/com/csaba79coder/userservice/model/service/UserService.java
@@ -0,0 +1,121 @@
+package com.csaba79coder.userservice.model.service;
+
+import com.csaba79coder.bestprotocol.model.UserModel;
+import com.csaba79coder.bestprotocol.model.UserModifyModel;
+import com.csaba79coder.bestprotocol.model.UserNewModel;
+import com.csaba79coder.userservice.model.entity.User;
+import com.csaba79coder.userservice.model.persistence.UserRepository;
+import com.csaba79coder.userservice.model.value.Availability;
+import com.csaba79coder.userservice.model.value.Role;
+import com.csaba79coder.userservice.util.Mapper;
+import com.csaba79coder.userservice.util.ValidationUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+/**
+ * responsible for the business logic
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class UserService {
+
+ private final UserRepository userRepository;
+
+ /**
+
+ Retrieves all users from the database and maps them to a list of user models.
+ @return a list of user models representing all users in the database
+ */
+ public List renderAllUsers() {
+ return userRepository.findAll()
+ .stream()
+ .map(Mapper::mapUserEntityToModel)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Adds a new user to the database if the email doesn't already exist,
+ * and if the email and password meet certain validation criteria.
+ * The new user is represented by a UserNewModel object.
+ *
+ * @param userNewModel the UserNewModel object representing the new user
+ * @return a UserModel object representing the newly added user
+ * @throws IllegalArgumentException if the email already exists, or if the
+ * email or password do not meet the validation criteria
+ */
+ public UserModel addNewUser(UserNewModel userNewModel) {
+ if (userRepository.findUserByEmail(userNewModel.getEmail()).isPresent()) {
+ String message = "Email already exists";
+ log.error(message);
+ throw new IllegalArgumentException(message);
+ } else {
+ if (ValidationUtil.isValidEmail(userNewModel.getEmail()) && ValidationUtil.isValidPassword(userNewModel.getPassword())) {
+ if (userNewModel.getPassword().equals(userNewModel.getRepeatPassword())) {
+ User user = new User();
+ user.setEmail(userNewModel.getEmail());
+ user.setUsername(userNewModel.getUsername());
+ user.setPassword(new BCryptPasswordEncoder().encode(userNewModel.getPassword()));
+ return Mapper.mapUserEntityToModel(userRepository.save(user));
+ } else {
+ String message = "Passwords do not match";
+ log.error(message);
+ throw new IllegalArgumentException(message);
+ }
+ } else {
+ String message = "Invalid email or password";
+ log.error(message);
+ throw new IllegalArgumentException(message);
+ }
+ }
+ }
+
+ /**
+ * @param userId
+ * @return a UserModel object from a User entity
+ */
+ public UserModel renderUserById(UUID userId) {
+ return Mapper.mapUserEntityToModel(userRepository.findUserById(userId).orElseThrow(() -> {
+ String message = "User not found";
+ log.error(message);
+ throw new IllegalArgumentException(message);
+ }));
+ }
+
+ /**
+ * @param userId
+ * @return a UserModel object from a modified User entity
+ */
+ public UserModifyModel modifyUserById(UUID userId, UserModel body) {
+ User user = userRepository.findUserById(userId).orElseThrow(() -> {
+ String message = "User not found";
+ log.error(message);
+ throw new IllegalArgumentException(message);
+ });
+ if (body.getEmail() != null) {
+ user.setEmail(body.getEmail());
+ }
+
+ if (body.getUsername() != null) {
+ user.setUsername(body.getUsername());
+ }
+
+ if (body.getAvailability() != null) {
+ user.setAvailability(Availability.valueOf(body.getAvailability().name()));
+ }
+
+ if (body.getRole() != null) {
+ user.setRole(Role.valueOf(body.getRole().name()));
+ }
+
+ user.setUpdatedAt(LocalDateTime.now());
+ return Mapper.mapUserEntityToModifyModel(userRepository.save(user));
+ }
+}
diff --git a/user-service/src/main/java/com/csaba79coder/userservice/model/value/Availability.java b/user-service/src/main/java/com/csaba79coder/userservice/model/value/Availability.java
new file mode 100644
index 0000000..009c98e
--- /dev/null
+++ b/user-service/src/main/java/com/csaba79coder/userservice/model/value/Availability.java
@@ -0,0 +1,13 @@
+package com.csaba79coder.userservice.model.value;
+
+/**
+
+ An enum representing the availability status of a User entity.
+
+ An entity can have one of three availability statuses: AVAILABLE, ARCHIVE, or DELETED.
+
+ */
+public enum Availability {
+
+ AVAILABLE, ARCHIVE, DELETED
+}
diff --git a/user-service/src/main/java/com/csaba79coder/userservice/model/value/Role.java b/user-service/src/main/java/com/csaba79coder/userservice/model/value/Role.java
new file mode 100644
index 0000000..b3c09a3
--- /dev/null
+++ b/user-service/src/main/java/com/csaba79coder/userservice/model/value/Role.java
@@ -0,0 +1,25 @@
+package com.csaba79coder.userservice.model.value;
+
+/**
+ * This enum represents the different roles that a user can have within
+ * the system. It contains three possible values: USER, ADMIN, and
+ * SUPER_ADMIN.
+ */
+public enum Role {
+
+ /**
+ * The USER role represents a regular user of the system.
+ */
+ USER,
+
+ /**
+ * The ADMIN role represents an administrator of the system.
+ */
+ ADMIN,
+
+ /**
+ * The SUPER_ADMIN role represents a super administrator of the
+ * system, with the highest level of privileges.
+ */
+ SUPER_ADMIN
+}
diff --git a/user-service/src/main/java/com/csaba79coder/userservice/util/Mapper.java b/user-service/src/main/java/com/csaba79coder/userservice/util/Mapper.java
new file mode 100644
index 0000000..bcdf798
--- /dev/null
+++ b/user-service/src/main/java/com/csaba79coder/userservice/util/Mapper.java
@@ -0,0 +1,34 @@
+package com.csaba79coder.userservice.util;
+
+import com.csaba79coder.bestprotocol.model.UserModel;
+import com.csaba79coder.bestprotocol.model.UserModifyModel;
+import com.csaba79coder.userservice.model.entity.User;
+import org.modelmapper.ModelMapper;
+
+/**
+ * responsible for mapping the entities to models
+ */
+public class Mapper {
+
+ private static final ModelMapper modelMapper = new ModelMapper();
+
+ /**
+ * maps the user entity to user model
+ *
+ * @param user the user entity
+ * @return the user model
+ */
+ public static UserModel mapUserEntityToModel(User user) {
+ return modelMapper.map(user, UserModel.class);
+ }
+
+ /**
+ * maps the user model to user entity
+ *
+ * @param user the user model
+ * @return the user entity
+ */
+ public static UserModifyModel mapUserEntityToModifyModel(User user) {
+ return modelMapper.map(user, UserModifyModel.class);
+ }
+}
diff --git a/user-service/src/main/java/com/csaba79coder/userservice/util/ValidationUtil.java b/user-service/src/main/java/com/csaba79coder/userservice/util/ValidationUtil.java
new file mode 100644
index 0000000..57f4c76
--- /dev/null
+++ b/user-service/src/main/java/com/csaba79coder/userservice/util/ValidationUtil.java
@@ -0,0 +1,41 @@
+package com.csaba79coder.userservice.util;
+
+import java.util.regex.Pattern;
+
+/**
+ * A utility class that provides methods for validating email addresses and passwords.
+ */
+public class ValidationUtil {
+
+ // A regular expression that matches valid email addresses
+ private static final Pattern EMAIL_PATTERN = Pattern.compile(
+ "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
+
+ /**
+ * Validates an email address. Returns true if the email is valid,
+ * and false otherwise.
+ *
+ * @param email the email address to validate
+ * @return true if the email is valid, false otherwise
+ */
+ public static boolean isValidEmail(String email) {
+ return EMAIL_PATTERN.matcher(email).matches();
+ }
+
+ /**
+ * Validates a password. Returns true if the password is valid,
+ * and false otherwise. A valid password must be at least 8 characters
+ * long and contain at least one uppercase letter, one lowercase letter,
+ * one digit, and one special character.
+ *
+ * @param password the password to validate
+ * @return true if the password is valid, false otherwise
+ */
+ public static boolean isValidPassword(String password) {
+ String pattern = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$";
+ return password != null && password.matches(pattern);
+ }
+
+ private ValidationUtil() {
+ }
+}
diff --git a/user-service/src/main/resources/application.properties b/user-service/src/main/resources/application.properties
new file mode 100644
index 0000000..9607ade
--- /dev/null
+++ b/user-service/src/main/resources/application.properties
@@ -0,0 +1,13 @@
+spring.jpa.hibernate.ddl-auto=update
+spring.datasource.url=jdbc:mysql://localhost:${DB_PORT_MYSQL}/${DB_URL_MYSQL_USER}?createDatabaseIfNotExist=true
+spring.datasource.username=${DB_USER_MYSQL}
+spring.datasource.password=${DB_PASSWORD_MYSQL}
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL55Dialect
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+spring.jpa.show-sql=true
+#spring.jpa.properties.hibernate.format_sql=true
+server.port=0
+
+eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka
+
+spring.application.name=user-service
\ No newline at end of file
diff --git a/user-service/src/main/resources/banner.txt b/user-service/src/main/resources/banner.txt
new file mode 100644
index 0000000..12e81a1
--- /dev/null
+++ b/user-service/src/main/resources/banner.txt
@@ -0,0 +1,16 @@
+${AnsiColor.BLUE}
+ ███████████ █████ ███████████ █████ ████
+░░███░░░░░███ ░░███ ░░███░░░░░███ ░░███ ░░███
+ ░███ ░███ ██████ █████ ███████ ░███ ░███ ████████ ██████ ███████ ██████ ██████ ██████ ░███
+ ░██████████ ███░░███ ███░░ ░░░███░ ░██████████ ░░███░░███ ███░░███░░░███░ ███░░███ ███░░███ ███░░███ ░███
+ ░███░░░░░███░███████ ░░█████ ░███ ░███░░░░░░ ░███ ░░░ ░███ ░███ ░███ ░███ ░███░███ ░░░ ░███ ░███ ░███
+ ░███ ░███░███░░░ ░░░░███ ░███ ███ ░███ ░███ ░███ ░███ ░███ ███░███ ░███░███ ███░███ ░███ ░███
+ ███████████ ░░██████ ██████ ░░█████ █████ █████ ░░██████ ░░█████ ░░██████ ░░██████ ░░██████ █████
+░░░░░░░░░░░ ░░░░░░ ░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░░ ░░░░░░ ░░░░░░ ░░░░░
+
+
+
+${spring-boot.formatted-version}
+${application.title}
+${application.version}
+
diff --git a/user-service/src/test/java/com/csaba79coder/userservice/UserControllerIT.java b/user-service/src/test/java/com/csaba79coder/userservice/UserControllerIT.java
new file mode 100644
index 0000000..cbf4840
--- /dev/null
+++ b/user-service/src/test/java/com/csaba79coder/userservice/UserControllerIT.java
@@ -0,0 +1,248 @@
+package com.csaba79coder.userservice;
+
+import com.csaba79coder.bestprotocol.model.UserModel;
+import com.csaba79coder.bestprotocol.model.UserNewModel;
+import com.csaba79coder.userservice.controller.UserController;
+import com.csaba79coder.userservice.model.base.entity.Identifier;
+import com.csaba79coder.userservice.model.entity.User;
+import com.csaba79coder.userservice.model.persistence.UserRepository;
+import com.csaba79coder.userservice.model.service.UserService;
+import com.csaba79coder.userservice.model.value.Availability;
+import com.csaba79coder.userservice.model.value.Role;
+import com.csaba79coder.userservice.util.Mapper;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.MediaType;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ * Integration test class for the {@link UserController}.
+ * This class tests the behavior of the UserController by sending mock HTTP requests
+ * and asserting the responses returned by the controller methods.
+ */
+@WebMvcTest(UserController.class)
+@ContextConfiguration(classes = {UserController.class})
+@ExtendWith(MockitoExtension.class)
+@SuppressWarnings("resource")
+public class UserControllerIT {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @MockBean
+ private UserService userService;
+ @MockBean
+ private UserRepository userRepository;
+
+ /**
+ * This method tests the behavior of the renderAllUsers() method of the UserController.
+ * It mocks the UserService to return a list of dummy users and then performs a GET request to the UserController endpoint.
+ * It asserts that the response status is 200 OK and the response body contains the expected list of users.
+ */
+ @Test
+ @DisplayName("GET /api/admin/user-service/users")
+ public void testRenderAllUsers() throws Exception {
+ // create some dummy user data
+ User user1 = new User();
+ user1.setEmail("user1@example.com");
+ user1.setUsername("user1");
+
+ User user2 = new User();
+ user2.setEmail("user2@example.com");
+ user2.setUsername("user2");
+
+ List userList = Arrays.asList(user1, user2);
+
+ // mock the userRepository to return the dummy data
+ when(userService.renderAllUsers()).thenReturn(
+ userList.stream().map(Mapper::mapUserEntityToModel).collect(Collectors.toList())
+ );
+
+ // perform the GET request to the UserController endpoint
+ MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/api/admin/user-service/users"))
+ .andExpect(status().isOk())
+ .andReturn();
+
+ // deserialize the response body to a List
+ List userModelList = objectMapper.readValue(
+ result.getResponse().getContentAsString(),
+ new TypeReference>() {
+ });
+
+ // assert that the userModelList is correct
+ assertEquals(userModelList.size(), userList.size());
+ assertEquals(userModelList.get(0).getEmail(), user1.getEmail());
+ assertEquals(userModelList.get(1).getEmail(), user2.getEmail());
+ assertEquals(userModelList.get(0).getUsername(), user1.getUsername());
+ assertEquals(userModelList.get(1).getUsername(), user2.getUsername());
+ }
+
+ /**
+ * This method tests the behavior of the addNewUser() method of the UserController.
+ * It mocks the UserService to return a dummy user and then performs a POST request to the UserController endpoint.
+ * It asserts that the response status is 201 CREATED and the response body contains the expected user.
+ */
+ @Test
+ @DisplayName("POST /api/admin/user-service/users")
+ public void testAddNewUser() throws Exception {
+ // create a userNewModel object
+ UserNewModel userNewModel = new UserNewModel();
+ userNewModel.setEmail("testuser@example.com");
+ userNewModel.setUsername("testuser");
+ userNewModel.setPassword("password");
+ userNewModel.setRepeatPassword("password");
+
+ // create a user object
+ User user = new User();
+ user.setEmail(userNewModel.getEmail());
+ user.setUsername(userNewModel.getUsername());
+ user.setPassword(new BCryptPasswordEncoder().encode(userNewModel.getPassword()));
+
+ // mock the userRepository to return the user object when save() is called
+ when(userService.addNewUser(userNewModel)).thenReturn(Mapper.mapUserEntityToModel(user));
+
+ // perform the POST request to the UserController endpoint with the userNewModel object as JSON
+ MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post("/api/admin/user-service/users")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(userNewModel)))
+ .andExpect(status().isCreated())
+ .andReturn();
+
+ // deserialize the response body to a UserModel object
+ UserModel userModel = objectMapper.readValue(
+ result.getResponse().getContentAsString(),
+ UserModel.class);
+
+ // assert that the userModel is correct
+ assertEquals(userModel.getEmail(), userNewModel.getEmail());
+ assertEquals(userModel.getUsername(), userNewModel.getUsername());
+ }
+
+ /**
+ * This method tests the behavior of the renderUserById() method of the UserController.
+ * It mocks the UserService to return a dummy user and then performs a GET request to the UserController endpoint.
+ * It asserts that the response status is 200 OK and the response body contains the expected user.
+ */
+ @Test
+ @DisplayName("GET /api/admin/user-service/users/{userId}")
+ public void testRenderUserById() throws Exception {
+ // create a dummy user
+ // create a dummy user
+ User user = new User();
+ user.setEmail("test@example.com");
+ user.setUsername("test");
+
+ // use reflection to set the ID field
+ Field idField = Identifier.class.getDeclaredField("id");
+ idField.setAccessible(true);
+ idField.set(user, UUID.randomUUID());
+
+ userRepository.save(user);
+ UUID userId = user.getId();
+
+ // mock the userRepository to return the dummy user
+ when(userService.renderUserById(userId)).thenReturn(Mapper.mapUserEntityToModel(user));
+
+ // perform the GET request to the UserController endpoint with the user id as a path variable
+ MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/api/admin/user-service/users/{userId}", userId))
+ .andExpect(status().isOk())
+ .andReturn();
+
+ // deserialize the response body to a UserModel
+ UserModel userModel = objectMapper.readValue(result.getResponse().getContentAsString(), UserModel.class);
+
+ // assert that the userModel is correct
+ assertEquals(userModel.getEmail(), user.getEmail());
+ assertEquals(userModel.getUsername(), user.getUsername());
+ assertEquals(userModel.getId(), user.getId());
+ }
+
+ /**
+ * This method tests the behavior of the modifyUserById() method of the UserController.
+ * It mocks the UserService to return a dummy user and then performs a PUT request to the UserController endpoint.
+ * It asserts that the response status is 200 OK and the response body contains the expected user.
+ */
+ @Test
+ @DisplayName("PUT /api/admin/user-service/users/{userId}")
+ void modifyUserByIdTest() throws Exception {
+ UUID userId = UUID.randomUUID();
+ UserModel userModel = new UserModel();
+ userModel.setEmail("new_email@example.com");
+ userModel.setUsername("new_username");
+ userModel.setRole(com.csaba79coder.bestprotocol.model.Role.ADMIN);
+ userModel.setAvailability(com.csaba79coder.bestprotocol.model.Availability.AVAILABLE);
+
+ User user = new User();
+ user.setEmail("old_email@example.com");
+ user.setUsername("old_username");
+ user.setRole(Role.USER);
+ user.setAvailability(Availability.AVAILABLE);
+
+ // use reflection to set the ID field
+ Field idField = Identifier.class.getDeclaredField("id");
+ idField.setAccessible(true);
+ idField.set(user, userId);
+
+ given(userRepository.findUserById(userId)).willReturn(Optional.of(user));
+ given(userRepository.save(user)).willReturn(user);
+
+ MvcResult mvcResult = mockMvc.perform(
+ (MockMvcRequestBuilders.put("/api/admin/user-service/users/{userId}", userId)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(asJsonString(userModel))))
+ .andExpect(status().isOk())
+ .andReturn();
+ }
+
+ // helper methods for serialization/deserialization
+ /**
+ * This method serializes an object to a JSON string.
+ * @param obj the object to serialize
+ * @return the JSON string
+ * @throws JsonProcessingException
+ */
+ private String asJsonString(Object obj) throws JsonProcessingException {
+ ObjectMapper mapper = new ObjectMapper();
+ return mapper.writeValueAsString(obj);
+ }
+
+ /**
+ * This method deserializes a JSON string to an object of the specified class.
+ * @param json the JSON string to deserialize
+ * @param clazz the class of the object to deserialize to
+ * @param the type of the object to deserialize to
+ * @return the deserialized object
+ * @throws JsonProcessingException
+ */
+ private T asObjectFromJsonString(String json, Class clazz) throws JsonProcessingException {
+ ObjectMapper mapper = new ObjectMapper();
+ return mapper.readValue(json, clazz);
+ }
+}
diff --git a/user-service/src/test/java/com/csaba79coder/userservice/util/MapperTest.java b/user-service/src/test/java/com/csaba79coder/userservice/util/MapperTest.java
new file mode 100644
index 0000000..3520b26
--- /dev/null
+++ b/user-service/src/test/java/com/csaba79coder/userservice/util/MapperTest.java
@@ -0,0 +1,55 @@
+package com.csaba79coder.userservice.util;
+
+import com.csaba79coder.bestprotocol.model.UserModel;
+import com.csaba79coder.bestprotocol.model.UserModifyModel;
+import com.csaba79coder.userservice.model.entity.User;
+import com.csaba79coder.userservice.model.value.Availability;
+import com.csaba79coder.userservice.model.value.Role;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * responsible for testing the mapper
+ */
+class MapperTest {
+
+ /**
+ * tests the mapUserEntityToModel method
+ */
+ @Test
+ public void testMapUserEntityToModel() {
+ User user = new User();
+ user.setUsername("john.doe");
+ user.setEmail("john.doe@example.com");
+ user.setAvailability(Availability.AVAILABLE);
+ user.setRole(Role.USER);
+
+ UserModel userModel = Mapper.mapUserEntityToModel(user);
+
+ assertEquals(user.getId(), userModel.getId());
+ assertEquals(user.getUsername(), userModel.getUsername());
+ assertEquals(user.getEmail(), userModel.getEmail());
+ assertEquals(user.getAvailability().name(), userModel.getAvailability().name());
+ assertEquals(user.getRole().name(), userModel.getRole().name());
+ }
+
+ /**
+ * tests the mapUserEntityToModifyModel method
+ */
+ @Test
+ public void testMapUserEntityToModifyModel() {
+ User user = new User();
+ user.setUsername("john.doe");
+ user.setEmail("john.doe@example.com");
+ user.setAvailability(Availability.AVAILABLE);
+ user.setRole(Role.USER);
+
+ UserModifyModel userModifyModel = Mapper.mapUserEntityToModifyModel(user);
+
+ assertEquals(user.getUsername(), userModifyModel.getUsername());
+ assertEquals(user.getEmail(), userModifyModel.getEmail());
+ assertEquals(user.getAvailability().name(), userModifyModel.getAvailability().name());
+ assertEquals(user.getRole().name(), userModifyModel.getRole().name());
+ }
+}
\ No newline at end of file
diff --git a/user-service/src/test/java/com/csaba79coder/userservice/util/ValidationUtilTest.java b/user-service/src/test/java/com/csaba79coder/userservice/util/ValidationUtilTest.java
new file mode 100644
index 0000000..a3df4fc
--- /dev/null
+++ b/user-service/src/test/java/com/csaba79coder/userservice/util/ValidationUtilTest.java
@@ -0,0 +1,54 @@
+package com.csaba79coder.userservice.util;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+
+ Unit test for {@link ValidationUtil}.
+ */
+class ValidationUtilTest {
+
+ /**
+
+ Test case for valid email address.
+ */
+ @Test
+ @DisplayName("Test valid email")
+ public void testValidEmail() {
+ assertTrue(ValidationUtil.isValidEmail("john.doe@example.com"));
+ }
+
+ /**
+
+ Test case for invalid email address.
+ */
+ @Test
+ @DisplayName("Test invalid email")
+ public void testInvalidEmail() {
+ assertFalse(ValidationUtil.isValidEmail("johndoe@example"));
+ }
+
+ /**
+
+ Test case for valid password.
+ */
+ @Test
+ @DisplayName("Test valid password")
+ public void testValidPassword() {
+ assertTrue(ValidationUtil.isValidPassword("P@ssword1"));
+ }
+
+ /**
+
+ Test case for invalid password.
+ */
+ @Test
+ @DisplayName("Test invalid password")
+ public void testInvalidPassword() {
+ assertFalse(ValidationUtil.isValidPassword("password"));
+ }
+}
\ No newline at end of file
diff --git a/user-service/src/test/resources/application.properties b/user-service/src/test/resources/application.properties
new file mode 100644
index 0000000..144f708
--- /dev/null
+++ b/user-service/src/test/resources/application.properties
@@ -0,0 +1,5 @@
+spring.datasource.url=jdbc:h2:mem:test_representative_db
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
\ No newline at end of file