diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..389da54e
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,13 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: osopromadze
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..dd84ea78
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,38 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+**Smartphone (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Browser [e.g. stock browser, safari]
+ - Version [e.g. 22]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..bbcbbe7d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 00000000..61e19e15
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,25 @@
+#### Description
+
+Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
+
+Fixes # (issue)
+
+#### Type of change
+
+Please delete options that are not relevant.
+
+- [ ] Bug fix (non-breaking change which fixes an issue)
+- [ ] New feature (non-breaking change which adds functionality)
+- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
+- [ ] This change requires a documentation update
+
+#### How Has This Been Tested?
+
+Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.
+
+#### Checklist:
+
+- [ ] I have performed a self-review of my own code
+- [ ] I have made corresponding changes to the documentation
+- [ ] My changes generate no new warnings or errors
+- [ ] New and existing unit tests pass locally with my changes
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..e943cfb1
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,37 @@
+name: SonarCloud
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ types: [opened, synchronize, reopened]
+jobs:
+ build:
+ name: Build and analyze
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - name: Set up JDK 17
+ uses: actions/setup-java@v3
+ with:
+ java-version: 17
+ distribution: 'zulu'
+ - name: Cache SonarCloud packages
+ uses: actions/cache@v3
+ with:
+ path: ~/.sonar/cache
+ key: ${{ runner.os }}-sonar
+ restore-keys: ${{ runner.os }}-sonar
+ - name: Cache Maven packages
+ uses: actions/cache@v3
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2
+ - name: Build and analyze
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=osopromadze_Spring-Boot-Blog-REST-API
\ No newline at end of file
diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml
new file mode 100644
index 00000000..5e398432
--- /dev/null
+++ b/.github/workflows/greetings.yml
@@ -0,0 +1,13 @@
+name: Greetings
+
+on: [pull_request_target, issues]
+
+jobs:
+ greeting:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/first-interaction@v1
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ issue-message: 'Welcome @${{ github.actor }} to Spring-Boot-Blog-REST-API community! Thanks so much for creating your first issue :)'
+ pr-message: 'Thanks so much @${{ github.actor }} for creating your first PR, the Spring-Boot-Blog-REST-API community thanks you :)'
diff --git a/.gitignore b/.gitignore
index 2d26c9f1..afea6911 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,12 @@
.settings
.springBeans
.sts4-cache
+/bin/
+/build/
+*.jar
+*.war
+*.ear
+*.db
### IntelliJ IDEA ###
.idea
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
deleted file mode 100644
index 9cc84ea9..00000000
Binary files a/.mvn/wrapper/maven-wrapper.jar and /dev/null differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
deleted file mode 100644
index 6c8c0e08..00000000
--- a/.mvn/wrapper/maven-wrapper.properties
+++ /dev/null
@@ -1 +0,0 @@
-distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..e7d65cce
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,5 @@
+FROM adoptopenjdk/openjdk11:alpine-jre
+VOLUME /tmp
+ARG JAR_FILE=target/*.jar
+COPY ${JAR_FILE} app.jar
+ENTRYPOINT ["java","-jar","/app.jar"]
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index b2752525..0ad25db4 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,661 @@
-MIT License
-
-Copyright (c) 2019 Omari Sopromadze
-
-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.
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
diff --git a/README.md b/README.md
index 7eb05f34..2906f0fd 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+[](https://travis-ci.com/coma123/Spring-Boot-Blog-REST-API) [](https://sonarcloud.io/dashboard?id=coma123_Spring-Boot-Blog-REST-API) [](https://bestpractices.coreinfrastructure.org/projects/3706)
+
# Spring Boot, MySQL, Spring Security, JWT, JPA, Rest API
Build Restful CRUD API for a blog using Spring Boot, Mysql, JPA and Hibernate.
@@ -284,5 +286,4 @@ Test them using postman or any other rest client.
"completed": true
}
```
-
-
+
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..60f6349a
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,9 @@
+# Security Policy
+
+We take security very seriously and try to patch each discovered security vulnerability as soon as possible. Also we use
+[automated tools](https://dependabot.com/) to continually monitor vulnerable dependencies.
+
+## Reporting a Vulnerability
+
+We use [full disclosure]() for reporting vulnerabilities. Therefore please
+submit vulnerability reports through our [issues section](https://github.com/coma123/Spring-Boot-Blog-REST-API/issues/new?assignees=&labels=&template=bug_report.md&title=).
diff --git a/data/blogapi.sql b/data/blogapi.sql
new file mode 100644
index 00000000..5cc9ced8
--- /dev/null
+++ b/data/blogapi.sql
@@ -0,0 +1,192 @@
+USE blogapi;
+
+UNLOCK TABLES;
+
+DROP TABLE IF EXISTS `post_tag`;
+DROP TABLE IF EXISTS `tags`;
+DROP TABLE IF EXISTS `user_role`;
+DROP TABLE IF EXISTS `roles`;
+DROP TABLE IF EXISTS `comments`;
+DROP TABLE IF EXISTS `posts`;
+DROP TABLE IF EXISTS `photos`;
+DROP TABLE IF EXISTS `albums`;
+DROP TABLE IF EXISTS `todos`;
+DROP TABLE IF EXISTS `users`;
+DROP TABLE IF EXISTS `address`;
+DROP TABLE IF EXISTS `company`;
+DROP TABLE IF EXISTS `geo`;
+
+CREATE TABLE `tags` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `name` varchar(255) NOT NULL,
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_by` bigint(19) unsigned NOT NULL,
+ `updated_by` bigint(19) unsigned NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE `geo` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `lat` varchar(255),
+ `lng` varchar(255),
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_by` bigint(19) unsigned DEFAULT NULL,
+ `updated_by` bigint(19) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
+
+CREATE TABLE `company` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `name` varchar(255),
+ `catch_phrase` varchar(255),
+ `bs` varchar(255),
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_by` bigint(19) unsigned DEFAULT NULL,
+ `updated_by` bigint(19) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
+
+CREATE TABLE `address` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `street` varchar(255),
+ `suite` varchar(255),
+ `city` varchar(255),
+ `zipcode` varchar(255),
+ `geo_id` bigint(19) unsigned DEFAULT NULL,
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_by` bigint(19) unsigned DEFAULT NULL,
+ `updated_by` bigint(19) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `fk_geo` (`geo_id`),
+ CONSTRAINT `fk_geo` FOREIGN KEY (`geo_id`) REFERENCES `geo` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
+
+CREATE TABLE `users` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `first_name` varchar(255) NOT NULL,
+ `last_name` varchar(255) NOT NULL,
+ `username` varchar(255) NOT NULL,
+ `password` varchar(255) NOT NULL,
+ `email` varchar(255) NOT NULL,
+ `address_id` bigint(19) unsigned DEFAULT NULL,
+ `phone` varchar(255),
+ `website` varchar(255),
+ `company_id` bigint(19) unsigned DEFAULT NULL,
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`),
+ KEY `fk_address` (`address_id`),
+ KEY `fk_company` (`company_id`),
+ CONSTRAINT `fk_address` FOREIGN KEY (`address_id`) REFERENCES `address` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `fk_company` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
+
+CREATE TABLE `todos` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `title` varchar(255) NOT NULL,
+ `completed` boolean default false,
+ `user_id` bigint(19) unsigned DEFAULT NULL,
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_by` bigint(19) unsigned DEFAULT NULL,
+ `updated_by` bigint(19) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `fk_user_todos` (`user_id`),
+ CONSTRAINT `fk_user_todos` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
+
+CREATE TABLE `albums` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `title` varchar(255) NOT NULL,
+ `user_id` bigint(19) unsigned DEFAULT NULL,
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_by` bigint(19) unsigned DEFAULT NULL,
+ `updated_by` bigint(19) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `fk_user_album` (`user_id`),
+ CONSTRAINT `fk_user_album` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
+
+CREATE TABLE `photos` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `title` varchar(255) NOT NULL,
+ `url` varchar(255) NOT NULL,
+ `thumbnail_url` varchar(255) NOT NULL,
+ `album_id` bigint(19) unsigned DEFAULT NULL,
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_by` bigint(19) unsigned DEFAULT NULL,
+ `updated_by` bigint(19) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `fk_album` (`album_id`),
+ CONSTRAINT `fk_album` FOREIGN KEY (`album_id`) REFERENCES `albums` (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
+
+CREATE TABLE `posts` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `title` varchar(255) NOT NULL,
+ `body` text NOT NULL,
+ `user_id` bigint(19) unsigned DEFAULT NULL,
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_by` bigint(19) unsigned DEFAULT NULL,
+ `updated_by` bigint(19) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `fk_user_post` (`user_id`),
+ CONSTRAINT `fk_user_post` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
+
+CREATE TABLE `post_tag` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `post_id` bigint(19) unsigned NOT NULL,
+ `tag_id` bigint(19) unsigned NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `fk_posttag_post_id` (`post_id`),
+ KEY `fk_posttag_tag_id` (`tag_id`),
+ CONSTRAINT `fk_posttag_post_id` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`),
+ CONSTRAINT `fk_posttag_tag_id` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE `comments` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `name` varchar(255) NOT NULL,
+ `email` varchar(255) NOT NULL,
+ `body` text NOT NULL,
+ `post_id` bigint(19) unsigned DEFAULT NULL,
+ `user_id` bigint(19) unsigned DEFAULT NULL,
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `created_by` bigint(19) unsigned NOT NULL,
+ `updated_by` bigint(19) unsigned NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `fk_comment_post` (`post_id`),
+ KEY `fk_comment_user` (`user_id`),
+ CONSTRAINT `fk_comment_post` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`),
+ CONSTRAINT `fk_comment_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
+
+CREATE TABLE `roles` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `name` varchar(255) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
+
+CREATE TABLE `user_role` (
+ `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT,
+ `user_id` bigint(19) unsigned NOT NULL,
+ `role_id` bigint(19) unsigned NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `fk_security_user_id` (`user_id`),
+ KEY `fk_security_role_id` (`role_id`),
+ CONSTRAINT `fk_security_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
+ CONSTRAINT `fk_security_role_id` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
+
+LOCK TABLES `roles` WRITE;
+INSERT INTO `roles` VALUES (1,'ROLE_ADMIN'),(2,'ROLE_USER');
+UNLOCK TABLES;
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..9bd9053e
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,36 @@
+version: '3.8'
+services:
+ db:
+ image: mysql
+ container_name: blogapi-db
+ restart: always
+ environment:
+ MYSQL_DATABASE: 'blogapi'
+ MYSQL_PASSWORD: 'root'
+ MYSQL_ROOT_PASSWORD: 'root'
+ ports:
+ - '3306:3306'
+ networks:
+ - blogapi-network
+ healthcheck:
+ test: "/usr/bin/mysql --user=root --password=root --execute \"SHOW DATABASES;\""
+ interval: 2s
+ timeout: 20s
+ retries: 10
+ volumes:
+ - ./data:/docker-entrypoint-initdb.d
+ application:
+ container_name: blogapi-application
+ build:
+ context: ./
+ dockerfile: Dockerfile
+ ports:
+ - "8080:8080"
+ networks:
+ - blogapi-network
+ depends_on:
+ - "db"
+networks:
+ blogapi-network:
+ name: blogapi-network
+ driver: bridge
diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..f9832f18
--- /dev/null
+++ b/docs/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at coma.spurs@gmail.com. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
new file mode 100644
index 00000000..5e2d16eb
--- /dev/null
+++ b/docs/CONTRIBUTING.md
@@ -0,0 +1,25 @@
+# :snowman: How to Contribute :snowman:
+
+We are quite glad you made it here! We can only keep our project in good health thanks to contributors like you! There are some points
+we would like to highlight in terms of contributing to this repository.
+
+## Bug Reports and New Features
+
+- If you have any issue please report that using [Github Issues](https://github.com/coma123/Spring-Boot-Blog-REST-API/issues).
+Currently there's two kinds; Bug Reports and New Features.
+
+- When writing a Bug Report or asking for a New Feature, please follow the template provided. Fill in the sections as much as you could so
+we have all teh information we need.
+
+## Coding Contributions
+
+- All coding contributions should be done through [pull requests](https://help.github.com/en/articles/creating-a-pull-request-from-a-fork). We do not respond to emails or coding posted via our Gitter channel. If you have any suggessions or want to contribute please open a [issue](https://github.com/coma123/Spring-Boot-Blog-REST-API/issues) first. This way we could discuss about the change and once finalized you could do a pull request.
+
+- We don't have a comprehensive style guide for now, but we expect you code with due deligence with standard practices. Your code
+should pass the standard unit and integration tests (automatically run by Travis).
+
+- Also please make sure your tests pass the [Sonar](https://sonarcloud.io/dashboard?id=coma123_Spring-Boot-Blog-REST-API) quality
+analysis. We strive to maintain A grade for all sections.
+
+:snowman: This is all for now. Hope to update this document as we go along. If you have any suggestions please feel free to open
+an issue in our [issue tracker](https://github.com/coma123/Spring-Boot-Blog-REST-API/issues). :snowman:
diff --git a/pom.xml b/pom.xml
index d08c5efd..4b6fd187 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,5 +1,6 @@
-
4.0.0
@@ -14,7 +15,7 @@
org.springframework.boot
spring-boot-starter-parent
- 2.0.6.RELEASE
+ 2.1.8.RELEASE
@@ -22,6 +23,8 @@
UTF-8
UTF-8
1.8
+ coma123
+ https://sonarcloud.io
@@ -36,101 +39,126 @@
runtime
-
- org.springframework.boot
- spring-boot-starter-web
- 2.0.6.RELEASE
-
-
-
- org.springframework.boot
- spring-boot-devtools
- 2.0.6.RELEASE
-
-
-
- org.springframework.boot
- spring-boot-starter-data-jpa
- 2.0.6.RELEASE
-
-
+
+ org.projectlombok
+ lombok
+ 1.18.12
+ provided
+
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
+
+ org.springframework.boot
+ spring-boot-starter-web
+
org.springframework.boot
- spring-boot-starter-security
- 2.0.6.RELEASE
-
-
-
- org.springframework.security
- spring-security-jwt
- 1.0.9.RELEASE
-
-
-
- org.springframework.security.oauth
- spring-security-oauth2
- 2.3.4.RELEASE
-
-
-
- io.jsonwebtoken
- jjwt
- 0.9.1
-
-
-
- javax.xml.bind
- jaxb-api
- 2.3.1
-
-
-
- com.fasterxml.jackson.datatype
- jackson-datatype-jsr310
- 2.9.7
+ spring-boot-starter-aop
-
-
- org.apache.commons
- commons-lang3
- 3.8.1
-
-
-
- org.modelmapper
- modelmapper
- 2.3.1
-
-
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
- 2.1.0.RELEASE
-
- com.sopromadze.blogapi.BlogApiApplication
-
-
-
-
- repackage
-
-
-
-
-
-
-
-
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+ org.mockito
+ mockito-all
+ 2.0.2-beta
+ test
+
+
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+
+
+
+ org.hamcrest
+ hamcrest-library
+ test
+
+
+
+
+ org.modelmapper
+ modelmapper
+ 2.3.5
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ org.springframework.security
+ spring-security-jwt
+ 1.0.9.RELEASE
+
+
+
+ org.springframework.security.oauth
+ spring-security-oauth2
+ [2.3.5,)
+
+
+
+ io.jsonwebtoken
+ jjwt
+ 0.9.1
+
+
+
+ javax.xml.bind
+ jaxb-api
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ com.sopromadze.blogapi.BlogApiApplication
+
+
+
+
+ repackage
+
+
+
+
+
+
diff --git a/sonar-project.properties b/sonar-project.properties
new file mode 100644
index 00000000..c11351b8
--- /dev/null
+++ b/sonar-project.properties
@@ -0,0 +1,2 @@
+sonar.branch.name=development
+sonar.sources=src/main/
diff --git a/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java b/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java
index 2f6e2b6e..6e45ce6b 100644
--- a/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java
+++ b/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java
@@ -1,6 +1,7 @@
package com.sopromadze.blogapi;
import com.sopromadze.blogapi.security.JwtAuthenticationFilter;
+import org.modelmapper.ModelMapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
@@ -11,24 +12,27 @@
import java.util.TimeZone;
@SpringBootApplication
-@EntityScan(basePackageClasses = {
- BlogApiApplication.class,
- Jsr310Converters.class
-})
+@EntityScan(basePackageClasses = { BlogApiApplication.class, Jsr310Converters.class })
+
public class BlogApiApplication {
- public static void main(String[] args) {
- SpringApplication.run(BlogApiApplication.class, args);
- }
+ public static void main(String[] args) {
+ SpringApplication.run(BlogApiApplication.class, args);
+ }
+
+ @PostConstruct
+ void init() {
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ }
- @PostConstruct
- void init(){
- TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
- }
+ @Bean
+ public JwtAuthenticationFilter jwtAuthenticationFilter() {
+ return new JwtAuthenticationFilter();
+ }
- @Bean
- public JwtAuthenticationFilter jwtAuthenticationFilter() {
- return new JwtAuthenticationFilter();
- }
+ @Bean
+ public ModelMapper modelMapper() {
+ return new ModelMapper();
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/config/AuditingConfig.java b/src/main/java/com/sopromadze/blogapi/config/AuditingConfig.java
index 2d820eae..144a9444 100644
--- a/src/main/java/com/sopromadze/blogapi/config/AuditingConfig.java
+++ b/src/main/java/com/sopromadze/blogapi/config/AuditingConfig.java
@@ -15,24 +15,24 @@
@EnableJpaAuditing
public class AuditingConfig {
- @Bean
- public AuditorAware auditorProvider(){
- return new SpringSecurityAuditAwareImpl();
- }
+ @Bean
+ public AuditorAware auditorProvider() {
+ return new SpringSecurityAuditAwareImpl();
+ }
}
-class SpringSecurityAuditAwareImpl implements AuditorAware{
+class SpringSecurityAuditAwareImpl implements AuditorAware {
- @Override
- public Optional getCurrentAuditor() {
- Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ @Override
+ public Optional getCurrentAuditor() {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
- if(authentication == null || !authentication.isAuthenticated() || authentication instanceof AnonymousAuthenticationToken) {
- return Optional.empty();
- }
+ if (authentication == null || !authentication.isAuthenticated() || authentication instanceof AnonymousAuthenticationToken) {
+ return Optional.empty();
+ }
- UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
+ UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
- return Optional.ofNullable(userPrincipal.getId());
- }
+ return Optional.ofNullable(userPrincipal.getId());
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/config/SecutiryConfig.java b/src/main/java/com/sopromadze/blogapi/config/SecutiryConfig.java
index de4b402f..1a0c1175 100644
--- a/src/main/java/com/sopromadze/blogapi/config/SecutiryConfig.java
+++ b/src/main/java/com/sopromadze/blogapi/config/SecutiryConfig.java
@@ -3,7 +3,7 @@
import com.sopromadze.blogapi.repository.UserRepository;
import com.sopromadze.blogapi.security.JwtAuthenticationEntryPoint;
import com.sopromadze.blogapi.security.JwtAuthenticationFilter;
-import com.sopromadze.blogapi.service.CustomUserDetailsService;
+import com.sopromadze.blogapi.service.impl.CustomUserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -23,54 +23,54 @@
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
- securedEnabled = true,
- jsr250Enabled = true,
- prePostEnabled = true)
+ securedEnabled = true,
+ jsr250Enabled = true,
+ prePostEnabled = true)
public class SecutiryConfig extends WebSecurityConfigurerAdapter {
- private final CustomUserDetailsService customUserDetailsService;
- private final JwtAuthenticationEntryPoint unauthorizedHandler;
- private final JwtAuthenticationFilter jwtAuthenticationFilter;
+ private final CustomUserDetailsServiceImpl customUserDetailsService;
+ private final JwtAuthenticationEntryPoint unauthorizedHandler;
+ private final JwtAuthenticationFilter jwtAuthenticationFilter;
- @Autowired
- public SecutiryConfig(UserRepository userRepository, CustomUserDetailsService customUserDetailsService, JwtAuthenticationEntryPoint unauthorizedHandler, JwtAuthenticationFilter jwtAuthenticationFilter) {
- this.customUserDetailsService = customUserDetailsService;
- this.unauthorizedHandler = unauthorizedHandler;
- this.jwtAuthenticationFilter = jwtAuthenticationFilter;
- ;
- }
+ @Autowired
+ public SecutiryConfig(UserRepository userRepository, CustomUserDetailsServiceImpl customUserDetailsService,
+ JwtAuthenticationEntryPoint unauthorizedHandler, JwtAuthenticationFilter jwtAuthenticationFilter) {
+ this.customUserDetailsService = customUserDetailsService;
+ this.unauthorizedHandler = unauthorizedHandler;
+ this.jwtAuthenticationFilter = jwtAuthenticationFilter;
+ }
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.cors().and().csrf().disable()
- .exceptionHandling()
- .authenticationEntryPoint(unauthorizedHandler)
- .and()
- .sessionManagement()
- .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
- .and()
- .authorizeRequests()
- .antMatchers(HttpMethod.GET, "/api/**").permitAll()
- .antMatchers("/api/auth/**").permitAll()
- .antMatchers(HttpMethod.GET, "/api/users/checkUsernameAvailability", "/api/users/checkEmailAvailability").permitAll()
- .anyRequest().authenticated();
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
- http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+ http.cors().and().csrf().disable()
+ .exceptionHandling()
+ .authenticationEntryPoint(unauthorizedHandler)
+ .and()
+ .sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ .and()
+ .authorizeRequests()
+ .antMatchers(HttpMethod.GET, "/api/**").permitAll()
+ .antMatchers(HttpMethod.POST, "/api/auth/**").permitAll()
+ .antMatchers(HttpMethod.GET, "/api/users/checkUsernameAvailability", "/api/users/checkEmailAvailability").permitAll()
+ .anyRequest().authenticated();
+ http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
- }
+ }
- public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
- authenticationManagerBuilder.userDetailsService(customUserDetailsService)
- .passwordEncoder(passwordEncoder());
- }
+ public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
+ authenticationManagerBuilder.userDetailsService(customUserDetailsService)
+ .passwordEncoder(passwordEncoder());
+ }
- @Bean(BeanIds.AUTHENTICATION_MANAGER)
- public AuthenticationManager authenticationManagerBean() throws Exception {
- return super.authenticationManagerBean();
- }
+ @Bean(BeanIds.AUTHENTICATION_MANAGER)
+ public AuthenticationManager authenticationManagerBean() throws Exception {
+ return super.authenticationManagerBean();
+ }
- @Bean
- public PasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/config/WebMvcConfig.java b/src/main/java/com/sopromadze/blogapi/config/WebMvcConfig.java
index eb9f2e79..301ecb17 100644
--- a/src/main/java/com/sopromadze/blogapi/config/WebMvcConfig.java
+++ b/src/main/java/com/sopromadze/blogapi/config/WebMvcConfig.java
@@ -1,17 +1,23 @@
package com.sopromadze.blogapi.config;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
- private final long MAX_AGE_SECS = 3600;
- public void addCorsMappings(CorsRegistry registry){
- registry.addMapping("/**")
- .allowedOrigins("*")
- .allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE")
- .maxAge(MAX_AGE_SECS);
- }
+ @Value("cors.allowedOrings")
+ private String allowedOrigins;
+
+ public void addCorsMappings(CorsRegistry registry) {
+ final long MAX_AGE_SECS = 3600;
+
+ registry.addMapping("/**")
+ .allowedOrigins(allowedOrigins)
+ .allowedMethods("GET", "POST", "PUT", "DELETE")
+ .allowedHeaders("*")
+ .maxAge(MAX_AGE_SECS);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java b/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java
index f90c3e08..b5dc9ff4 100644
--- a/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java
+++ b/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java
@@ -1,67 +1,90 @@
package com.sopromadze.blogapi.controller;
-import com.sopromadze.blogapi.model.album.Album;
+import com.sopromadze.blogapi.exception.ResponseEntityErrorException;
+import com.sopromadze.blogapi.model.Album;
+import com.sopromadze.blogapi.payload.AlbumResponse;
+import com.sopromadze.blogapi.payload.ApiResponse;
import com.sopromadze.blogapi.payload.PagedResponse;
+import com.sopromadze.blogapi.payload.PhotoResponse;
+import com.sopromadze.blogapi.payload.request.AlbumRequest;
import com.sopromadze.blogapi.security.CurrentUser;
import com.sopromadze.blogapi.security.UserPrincipal;
import com.sopromadze.blogapi.service.AlbumService;
import com.sopromadze.blogapi.service.PhotoService;
-import com.sopromadze.blogapi.util.AppConstants;
+import com.sopromadze.blogapi.utils.AppConstants;
+import com.sopromadze.blogapi.utils.AppUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping("/api/albums")
public class AlbumController {
- private final AlbumService albumService;
- private final PhotoService photoService;
+ @Autowired
+ private AlbumService albumService;
- @Autowired
- public AlbumController(AlbumService albumService, PhotoService photoService) {
- this.albumService = albumService;
- this.photoService = photoService;
- }
+ @Autowired
+ private PhotoService photoService;
- @GetMapping
- public PagedResponse getAllAlbums(
- @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
- @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size){
- return albumService.getAllAlbums(page, size);
- }
+ @ExceptionHandler(ResponseEntityErrorException.class)
+ public ResponseEntity handleExceptions(ResponseEntityErrorException exception) {
+ return exception.getApiResponse();
+ }
- @PostMapping
- @PreAuthorize("hasRole('USER')")
- public ResponseEntity> addAlbum(@Valid @RequestBody Album album, @CurrentUser UserPrincipal currentUser){
- return albumService.addAlbum(album, currentUser);
- }
+ @GetMapping
+ public PagedResponse getAllAlbums(
+ @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
+ @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) {
+ AppUtils.validatePageNumberAndSize(page, size);
- @GetMapping("/{id}")
- public ResponseEntity> getAlbum(@PathVariable(name = "id") Long id){
- return albumService.getAlbum(id);
- }
+ return albumService.getAllAlbums(page, size);
+ }
- @PutMapping("/{id}")
- @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
- public ResponseEntity> updateAlbum(@PathVariable(name = "id") Long id, @Valid @RequestBody Album newAlbum, @CurrentUser UserPrincipal currentUser){
- return albumService.updateAlbum(id, newAlbum, currentUser);
- }
+ @PostMapping
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity addAlbum(@Valid @RequestBody AlbumRequest albumRequest, @CurrentUser UserPrincipal currentUser) {
+ return albumService.addAlbum(albumRequest, currentUser);
+ }
- @DeleteMapping("/{id}")
- @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
- public ResponseEntity> deleteAlbum(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser){
- return albumService.deleteAlbum(id, currentUser);
- }
+ @GetMapping("/{id}")
+ public ResponseEntity getAlbum(@PathVariable(name = "id") Long id) {
+ return albumService.getAlbum(id);
+ }
- @GetMapping("/{id}/photos")
- public PagedResponse> getAllPhotosByAlbum(
- @PathVariable(name = "id") Long id,
- @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
- @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size){
- return photoService.getAllPhotosByAlbum(id, page, size);
- }
+ @PutMapping("/{id}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity updateAlbum(@PathVariable(name = "id") Long id, @Valid @RequestBody AlbumRequest newAlbum,
+ @CurrentUser UserPrincipal currentUser) {
+ return albumService.updateAlbum(id, newAlbum, currentUser);
+ }
+
+ @DeleteMapping("/{id}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity deleteAlbum(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) {
+ return albumService.deleteAlbum(id, currentUser);
+ }
+
+ @GetMapping("/{id}/photos")
+ public ResponseEntity> getAllPhotosByAlbum(@PathVariable(name = "id") Long id,
+ @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
+ @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) {
+
+ PagedResponse response = photoService.getAllPhotosByAlbum(id, page, size);
+
+ return new ResponseEntity<>(response, HttpStatus.OK);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/controller/AuthController.java b/src/main/java/com/sopromadze/blogapi/controller/AuthController.java
index d0952d1f..5d49753d 100644
--- a/src/main/java/com/sopromadze/blogapi/controller/AuthController.java
+++ b/src/main/java/com/sopromadze/blogapi/controller/AuthController.java
@@ -1,6 +1,7 @@
package com.sopromadze.blogapi.controller;
import com.sopromadze.blogapi.exception.AppException;
+import com.sopromadze.blogapi.exception.BlogapiException;
import com.sopromadze.blogapi.model.role.Role;
import com.sopromadze.blogapi.model.role.RoleName;
import com.sopromadze.blogapi.model.user.User;
@@ -33,77 +34,75 @@
@RestController
@RequestMapping("/api/auth")
public class AuthController {
- private final AuthenticationManager authenticationManager;
+ private static final String USER_ROLE_NOT_SET = "User role not set";
- private final UserRepository userRepository;
+ @Autowired
+ private AuthenticationManager authenticationManager;
- private final RoleRepository roleRepository;
+ @Autowired
+ private UserRepository userRepository;
- private final PasswordEncoder passwordEncoder;
+ @Autowired
+ private RoleRepository roleRepository;
- private final JwtTokenProvider jwtTokenProvider;
+ @Autowired
+ private PasswordEncoder passwordEncoder;
- @Autowired
- public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, RoleRepository roleRepository, PasswordEncoder passwordEncoder, JwtTokenProvider jwtTokenProvider) {
- this.authenticationManager = authenticationManager;
- this.userRepository = userRepository;
- this.roleRepository = roleRepository;
- this.passwordEncoder = passwordEncoder;
- this.jwtTokenProvider = jwtTokenProvider;
- }
+ @Autowired
+ private JwtTokenProvider jwtTokenProvider;
- @PostMapping("/signin")
- public ResponseEntity> authenticateUser(@Valid @RequestBody LoginRequest loginRequest){
- Authentication authentication = authenticationManager.authenticate(
- new UsernamePasswordAuthenticationToken(
- loginRequest.getUsernameOrEmail(),
- loginRequest.getPassword()
- )
- );
+ @PostMapping("/signin")
+ public ResponseEntity authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
+ Authentication authentication = authenticationManager.authenticate(
+ new UsernamePasswordAuthenticationToken(loginRequest.getUsernameOrEmail(), loginRequest.getPassword()));
- SecurityContextHolder.getContext().setAuthentication(authentication);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
- String jwt = jwtTokenProvider.generateToken(authentication);
- return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
- }
+ String jwt = jwtTokenProvider.generateToken(authentication);
+ return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
+ }
- @PostMapping("/signup")
- public ResponseEntity> registerUser(@Valid @RequestBody SignUpRequest signUpRequest){
- if(userRepository.existsByUsername(signUpRequest.getUsername())){
- return new ResponseEntity<>(new ApiResponse(false, "Username is already taken"), HttpStatus.BAD_REQUEST);
- }
+ @PostMapping("/signup")
+ public ResponseEntity registerUser(@Valid @RequestBody SignUpRequest signUpRequest) {
+ if (Boolean.TRUE.equals(userRepository.existsByUsername(signUpRequest.getUsername()))) {
+ throw new BlogapiException(HttpStatus.BAD_REQUEST, "Username is already taken");
+ }
- if(userRepository.existsByEmail(signUpRequest.getEmail())){
- return new ResponseEntity<>(new ApiResponse(false, "Email is already taken"), HttpStatus.BAD_REQUEST);
- }
- String firstName = signUpRequest.getFirstName().substring(0, 1).toUpperCase() + signUpRequest.getFirstName().substring(1).toLowerCase();
+ if (Boolean.TRUE.equals(userRepository.existsByEmail(signUpRequest.getEmail()))) {
+ throw new BlogapiException(HttpStatus.BAD_REQUEST, "Email is already taken");
+ }
- String lastName = signUpRequest.getLastName().substring(0, 1).toUpperCase() + signUpRequest.getLastName().substring(1).toLowerCase();
+ String firstName = signUpRequest.getFirstName().toLowerCase();
- String username = signUpRequest.getUsername().toLowerCase();
+ String lastName = signUpRequest.getLastName().toLowerCase();
- String email = signUpRequest.getEmail().toLowerCase();
+ String username = signUpRequest.getUsername().toLowerCase();
- User user = new User(firstName, lastName, username, email, signUpRequest.getPassword());
+ String email = signUpRequest.getEmail().toLowerCase();
- user.setPassword(passwordEncoder.encode(user.getPassword()));
+ String password = passwordEncoder.encode(signUpRequest.getPassword());
- List roles = new ArrayList<>();
- if(userRepository.count() == 0){
- roles.add(roleRepository.findByName(RoleName.ROLE_USER).orElseThrow(() -> new AppException("User role not set")));
- roles.add(roleRepository.findByName(RoleName.ROLE_ADMIN).orElseThrow(() -> new AppException("User role not set")));
- } else{
- roles.add(roleRepository.findByName(RoleName.ROLE_USER).orElseThrow(() -> new AppException("User role not set")));
- }
+ User user = new User(firstName, lastName, username, email, password);
- user.setRoles(roles);
+ List roles = new ArrayList<>();
- User result = userRepository.save(user);
+ if (userRepository.count() == 0) {
+ roles.add(roleRepository.findByName(RoleName.ROLE_USER)
+ .orElseThrow(() -> new AppException(USER_ROLE_NOT_SET)));
+ roles.add(roleRepository.findByName(RoleName.ROLE_ADMIN)
+ .orElseThrow(() -> new AppException(USER_ROLE_NOT_SET)));
+ } else {
+ roles.add(roleRepository.findByName(RoleName.ROLE_USER)
+ .orElseThrow(() -> new AppException(USER_ROLE_NOT_SET)));
+ }
- URI location = ServletUriComponentsBuilder
- .fromCurrentContextPath().path("/api/users/{userId}")
- .buildAndExpand(result.getId()).toUri();
+ user.setRoles(roles);
- return ResponseEntity.created(location).body(new ApiResponse(true, "User registered successfully"));
- }
+ User result = userRepository.save(user);
+
+ URI location = ServletUriComponentsBuilder.fromCurrentContextPath().path("/api/users/{userId}")
+ .buildAndExpand(result.getId()).toUri();
+
+ return ResponseEntity.created(location).body(new ApiResponse(Boolean.TRUE, "User registered successfully"));
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java b/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java
new file mode 100644
index 00000000..39b63842
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java
@@ -0,0 +1,66 @@
+package com.sopromadze.blogapi.controller;
+
+import com.sopromadze.blogapi.exception.UnauthorizedException;
+import com.sopromadze.blogapi.model.Category;
+import com.sopromadze.blogapi.payload.ApiResponse;
+import com.sopromadze.blogapi.payload.PagedResponse;
+import com.sopromadze.blogapi.security.CurrentUser;
+import com.sopromadze.blogapi.security.UserPrincipal;
+import com.sopromadze.blogapi.service.CategoryService;
+import com.sopromadze.blogapi.utils.AppConstants;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+
+@RestController
+@RequestMapping("/api/categories")
+public class CategoryController {
+ @Autowired
+ private CategoryService categoryService;
+
+ @GetMapping
+ public PagedResponse getAllCategories(
+ @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
+ @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) {
+ return categoryService.getAllCategories(page, size);
+ }
+
+ @PostMapping
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity addCategory(@Valid @RequestBody Category category,
+ @CurrentUser UserPrincipal currentUser) {
+
+ return categoryService.addCategory(category, currentUser);
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getCategory(@PathVariable(name = "id") Long id) {
+ return categoryService.getCategory(id);
+ }
+
+ @PutMapping("/{id}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity updateCategory(@PathVariable(name = "id") Long id,
+ @Valid @RequestBody Category category, @CurrentUser UserPrincipal currentUser) throws UnauthorizedException {
+ return categoryService.updateCategory(id, category, currentUser);
+ }
+
+ @DeleteMapping("/{id}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity deleteCategory(@PathVariable(name = "id") Long id,
+ @CurrentUser UserPrincipal currentUser) throws UnauthorizedException {
+ return categoryService.deleteCategory(id, currentUser);
+ }
+
+}
diff --git a/src/main/java/com/sopromadze/blogapi/controller/CommentController.java b/src/main/java/com/sopromadze/blogapi/controller/CommentController.java
index f0375010..2a402910 100644
--- a/src/main/java/com/sopromadze/blogapi/controller/CommentController.java
+++ b/src/main/java/com/sopromadze/blogapi/controller/CommentController.java
@@ -1,63 +1,83 @@
package com.sopromadze.blogapi.controller;
-import com.sopromadze.blogapi.exception.ResourceNotFoundException;
-import com.sopromadze.blogapi.model.comment.Comment;
-import com.sopromadze.blogapi.model.post.Post;
+import com.sopromadze.blogapi.model.Comment;
+import com.sopromadze.blogapi.payload.ApiResponse;
import com.sopromadze.blogapi.payload.CommentRequest;
import com.sopromadze.blogapi.payload.PagedResponse;
-import com.sopromadze.blogapi.repository.CommentRepository;
-import com.sopromadze.blogapi.repository.PostRepository;
import com.sopromadze.blogapi.security.CurrentUser;
import com.sopromadze.blogapi.security.UserPrincipal;
import com.sopromadze.blogapi.service.CommentService;
-import com.sopromadze.blogapi.util.AppConstants;
+import com.sopromadze.blogapi.utils.AppConstants;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
-import java.util.List;
@RestController
@RequestMapping("/api/posts/{postId}/comments")
public class CommentController {
- private final CommentService commentService;
-
- @Autowired
- public CommentController(CommentService commentService) {
- this.commentService = commentService;
- }
-
- @GetMapping
- public PagedResponse> getAllComments(
- @PathVariable(name = "postId") Long postId,
- @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
- @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size){
- return commentService.getAllComments(postId, page, size);
- }
-
- @PostMapping
- @PreAuthorize("hasRole('USER')")
- public ResponseEntity> addComment(@Valid @RequestBody CommentRequest commentRequest, @PathVariable(name = "postId") Long postId, @CurrentUser UserPrincipal currentUser){
- return commentService.addComment(commentRequest, postId, currentUser);
- }
-
- @GetMapping("/{id}")
- public ResponseEntity> getComment(@PathVariable(name = "postId") Long postId, @PathVariable(name = "id") Long id){
- return commentService.getComment(postId, id);
- }
-
- @PutMapping("/{id}")
- @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
- public ResponseEntity> updateComment(@PathVariable(name = "postId") Long postId, @PathVariable(name = "id") Long id, @Valid @RequestBody CommentRequest commentRequest, @CurrentUser UserPrincipal currentUser){
- return commentService.updateComment(postId, id, commentRequest, currentUser);
- }
-
- @DeleteMapping("/{id}")
- @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
- public ResponseEntity> deleteComment(@PathVariable(name = "postId") Long postId, @PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser){
- return commentService.deleteComment(postId, id, currentUser);
- }
+ @Autowired
+ private CommentService commentService;
+
+ @GetMapping
+ public ResponseEntity> getAllComments(@PathVariable(name = "postId") Long postId,
+ @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
+ @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) {
+
+ PagedResponse allComments = commentService.getAllComments(postId, page, size);
+
+ return new ResponseEntity< >(allComments, HttpStatus.OK);
+ }
+
+ @PostMapping
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity addComment(@Valid @RequestBody CommentRequest commentRequest,
+ @PathVariable(name = "postId") Long postId, @CurrentUser UserPrincipal currentUser) {
+ Comment newComment = commentService.addComment(commentRequest, postId, currentUser);
+
+ return new ResponseEntity<>(newComment, HttpStatus.CREATED);
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getComment(@PathVariable(name = "postId") Long postId,
+ @PathVariable(name = "id") Long id) {
+ Comment comment = commentService.getComment(postId, id);
+
+ return new ResponseEntity<>(comment, HttpStatus.OK);
+ }
+
+ @PutMapping("/{id}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity updateComment(@PathVariable(name = "postId") Long postId,
+ @PathVariable(name = "id") Long id, @Valid @RequestBody CommentRequest commentRequest,
+ @CurrentUser UserPrincipal currentUser) {
+
+ Comment updatedComment = commentService.updateComment(postId, id, commentRequest, currentUser);
+
+ return new ResponseEntity<>(updatedComment, HttpStatus.OK);
+ }
+
+ @DeleteMapping("/{id}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity deleteComment(@PathVariable(name = "postId") Long postId,
+ @PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) {
+
+ ApiResponse response = commentService.deleteComment(postId, id, currentUser);
+
+ HttpStatus status = response.getSuccess() ? HttpStatus.OK : HttpStatus.BAD_REQUEST;
+
+ return new ResponseEntity<>(response, status);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java b/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java
index 24112ee9..2f0846d0 100644
--- a/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java
+++ b/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java
@@ -1,63 +1,73 @@
package com.sopromadze.blogapi.controller;
-import com.sopromadze.blogapi.exception.ResourceNotFoundException;
-import com.sopromadze.blogapi.model.album.Album;
-import com.sopromadze.blogapi.model.photo.Photo;
+import com.sopromadze.blogapi.payload.ApiResponse;
import com.sopromadze.blogapi.payload.PagedResponse;
import com.sopromadze.blogapi.payload.PhotoRequest;
import com.sopromadze.blogapi.payload.PhotoResponse;
-import com.sopromadze.blogapi.repository.AlbumRepository;
-import com.sopromadze.blogapi.repository.PhotoRepository;
import com.sopromadze.blogapi.security.CurrentUser;
import com.sopromadze.blogapi.security.UserPrincipal;
import com.sopromadze.blogapi.service.PhotoService;
-import com.sopromadze.blogapi.util.AppConstants;
+import com.sopromadze.blogapi.utils.AppConstants;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
-import java.util.ArrayList;
-import java.util.List;
@RestController
@RequestMapping("/api/photos")
public class PhotoController {
- private final PhotoService photoService;
-
- @Autowired
- public PhotoController(PhotoService photoService) {
- this.photoService = photoService;
- }
-
- @GetMapping
- public PagedResponse getAllPhotos(
- @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
- @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size){
- return photoService.getAllPhotos(page, size);
- }
-
- @PostMapping
- @PreAuthorize("hasRole('USER')")
- public ResponseEntity> addPhoto(@Valid @RequestBody PhotoRequest photoRequest, @CurrentUser UserPrincipal currentUser){
- return photoService.addPhoto(photoRequest, currentUser);
- }
-
- @GetMapping("/{id}")
- public ResponseEntity> getPhoto(@PathVariable(name = "id") Long id){
- return photoService.getPhoto(id);
- }
-
- @PutMapping("/{id}")
- @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
- public ResponseEntity> updatePhoto(@PathVariable(name = "id") Long id, @Valid @RequestBody PhotoRequest photoRequest, @CurrentUser UserPrincipal currentUser){
- return photoService.updatePhoto(id, photoRequest, currentUser);
- }
-
- @DeleteMapping("/{id}")
- @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
- public ResponseEntity> deletePhoto(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser){
- return photoService.deletePhoto(id, currentUser);
- }
+ @Autowired
+ private PhotoService photoService;
+
+ @GetMapping
+ public PagedResponse getAllPhotos(
+ @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
+ @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) {
+ return photoService.getAllPhotos(page, size);
+ }
+
+ @PostMapping
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity addPhoto(@Valid @RequestBody PhotoRequest photoRequest,
+ @CurrentUser UserPrincipal currentUser) {
+ PhotoResponse photoResponse = photoService.addPhoto(photoRequest, currentUser);
+
+ return new ResponseEntity< >(photoResponse, HttpStatus.OK);
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getPhoto(@PathVariable(name = "id") Long id) {
+ PhotoResponse photoResponse = photoService.getPhoto(id);
+
+ return new ResponseEntity< >(photoResponse, HttpStatus.OK);
+ }
+
+ @PutMapping("/{id}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity updatePhoto(@PathVariable(name = "id") Long id,
+ @Valid @RequestBody PhotoRequest photoRequest, @CurrentUser UserPrincipal currentUser) {
+
+ PhotoResponse photoResponse = photoService.updatePhoto(id, photoRequest, currentUser);
+
+ return new ResponseEntity< >(photoResponse, HttpStatus.OK);
+ }
+
+ @DeleteMapping("/{id}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity deletePhoto(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) {
+ ApiResponse apiResponse = photoService.deletePhoto(id, currentUser);
+
+ return new ResponseEntity< >(apiResponse, HttpStatus.OK);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/controller/PostController.java b/src/main/java/com/sopromadze/blogapi/controller/PostController.java
index 1bb02f0d..5b8efcc7 100644
--- a/src/main/java/com/sopromadze/blogapi/controller/PostController.java
+++ b/src/main/java/com/sopromadze/blogapi/controller/PostController.java
@@ -1,56 +1,95 @@
package com.sopromadze.blogapi.controller;
-import com.sopromadze.blogapi.model.post.Post;
+import com.sopromadze.blogapi.model.Post;
+import com.sopromadze.blogapi.payload.ApiResponse;
import com.sopromadze.blogapi.payload.PagedResponse;
+import com.sopromadze.blogapi.payload.PostRequest;
+import com.sopromadze.blogapi.payload.PostResponse;
import com.sopromadze.blogapi.security.CurrentUser;
import com.sopromadze.blogapi.security.UserPrincipal;
import com.sopromadze.blogapi.service.PostService;
-import com.sopromadze.blogapi.util.AppConstants;
+import com.sopromadze.blogapi.utils.AppConstants;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
-import java.util.Optional;
@RestController
@RequestMapping("/api/posts")
public class PostController {
- private final PostService postService;
-
- @Autowired
- public PostController(PostService postService) {
- this.postService = postService;
- }
-
- @GetMapping
- public PagedResponse getAllPosts(
- @RequestParam(value = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
- @RequestParam(value = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size){
- return postService.getAllPosts(page, size);
- }
-
- @PostMapping
- @PreAuthorize("hasRole('USER')")
- public ResponseEntity> addPost(@Valid @RequestBody Post post, @CurrentUser UserPrincipal currentUser){
- return postService.addPost(post, currentUser);
- }
-
- @GetMapping("/{id}")
- public ResponseEntity> getPost(@PathVariable(name = "id") Long id){
- return postService.getPost(id);
- }
-
- @PutMapping("/{id}")
- @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
- public ResponseEntity> updatePost(@PathVariable(name = "id") Long id, @Valid @RequestBody Post newPost, @CurrentUser UserPrincipal currentUser){
- return postService.updatePost(id, newPost, currentUser);
- }
-
- @DeleteMapping("/{id}")
- @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
- public ResponseEntity> deletePost(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser){
- return postService.deletePost(id, currentUser);
- }
+ @Autowired
+ private PostService postService;
+
+ @GetMapping
+ public ResponseEntity> getAllPosts(
+ @RequestParam(value = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
+ @RequestParam(value = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) {
+ PagedResponse response = postService.getAllPosts(page, size);
+
+ return new ResponseEntity< >(response, HttpStatus.OK);
+ }
+
+ @GetMapping("/category/{id}")
+ public ResponseEntity> getPostsByCategory(
+ @RequestParam(value = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
+ @RequestParam(value = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size,
+ @PathVariable(name = "id") Long id) {
+ PagedResponse response = postService.getPostsByCategory(id, page, size);
+
+ return new ResponseEntity< >(response, HttpStatus.OK);
+ }
+
+ @GetMapping("/tag/{id}")
+ public ResponseEntity> getPostsByTag(
+ @RequestParam(value = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
+ @RequestParam(value = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size,
+ @PathVariable(name = "id") Long id) {
+ PagedResponse response = postService.getPostsByTag(id, page, size);
+
+ return new ResponseEntity< >(response, HttpStatus.OK);
+ }
+
+ @PostMapping
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity addPost(@Valid @RequestBody PostRequest postRequest,
+ @CurrentUser UserPrincipal currentUser) {
+ PostResponse postResponse = postService.addPost(postRequest, currentUser);
+
+ return new ResponseEntity< >(postResponse, HttpStatus.CREATED);
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getPost(@PathVariable(name = "id") Long id) {
+ Post post = postService.getPost(id);
+
+ return new ResponseEntity< >(post, HttpStatus.OK);
+ }
+
+ @PutMapping("/{id}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity updatePost(@PathVariable(name = "id") Long id,
+ @Valid @RequestBody PostRequest newPostRequest, @CurrentUser UserPrincipal currentUser) {
+ Post post = postService.updatePost(id, newPostRequest, currentUser);
+
+ return new ResponseEntity< >(post, HttpStatus.OK);
+ }
+
+ @DeleteMapping("/{id}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity deletePost(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) {
+ ApiResponse apiResponse = postService.deletePost(id, currentUser);
+
+ return new ResponseEntity< >(apiResponse, HttpStatus.OK);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/controller/TagController.java b/src/main/java/com/sopromadze/blogapi/controller/TagController.java
new file mode 100644
index 00000000..9a9b45c8
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/controller/TagController.java
@@ -0,0 +1,74 @@
+package com.sopromadze.blogapi.controller;
+
+import com.sopromadze.blogapi.model.Tag;
+import com.sopromadze.blogapi.payload.ApiResponse;
+import com.sopromadze.blogapi.payload.PagedResponse;
+import com.sopromadze.blogapi.security.CurrentUser;
+import com.sopromadze.blogapi.security.UserPrincipal;
+import com.sopromadze.blogapi.service.TagService;
+import com.sopromadze.blogapi.utils.AppConstants;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+
+@RestController
+@RequestMapping("/api/tags")
+public class TagController {
+ @Autowired
+ private TagService tagService;
+
+ @GetMapping
+ public ResponseEntity> getAllTags(
+ @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
+ @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) {
+
+ PagedResponse response = tagService.getAllTags(page, size);
+
+ return new ResponseEntity< >(response, HttpStatus.OK);
+ }
+
+ @PostMapping
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity addTag(@Valid @RequestBody Tag tag, @CurrentUser UserPrincipal currentUser) {
+ Tag newTag = tagService.addTag(tag, currentUser);
+
+ return new ResponseEntity< >(newTag, HttpStatus.CREATED);
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getTag(@PathVariable(name = "id") Long id) {
+ Tag tag = tagService.getTag(id);
+
+ return new ResponseEntity< >(tag, HttpStatus.OK);
+ }
+
+ @PutMapping("/{id}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity updateTag(@PathVariable(name = "id") Long id, @Valid @RequestBody Tag tag, @CurrentUser UserPrincipal currentUser) {
+
+ Tag updatedTag = tagService.updateTag(id, tag, currentUser);
+
+ return new ResponseEntity< >(updatedTag, HttpStatus.OK);
+ }
+
+ @DeleteMapping("/{id}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity deleteTag(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) {
+ ApiResponse apiResponse = tagService.deleteTag(id, currentUser);
+
+ return new ResponseEntity< >(apiResponse, HttpStatus.OK);
+ }
+
+}
diff --git a/src/main/java/com/sopromadze/blogapi/controller/TodoController.java b/src/main/java/com/sopromadze/blogapi/controller/TodoController.java
index 0605ae63..aeb57789 100644
--- a/src/main/java/com/sopromadze/blogapi/controller/TodoController.java
+++ b/src/main/java/com/sopromadze/blogapi/controller/TodoController.java
@@ -1,72 +1,95 @@
package com.sopromadze.blogapi.controller;
-import com.sopromadze.blogapi.model.todo.Todo;
+import com.sopromadze.blogapi.model.Todo;
+import com.sopromadze.blogapi.payload.ApiResponse;
import com.sopromadze.blogapi.payload.PagedResponse;
import com.sopromadze.blogapi.security.CurrentUser;
import com.sopromadze.blogapi.security.UserPrincipal;
import com.sopromadze.blogapi.service.TodoService;
-import com.sopromadze.blogapi.util.AppConstants;
+import com.sopromadze.blogapi.utils.AppConstants;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
-import java.util.Optional;
@RestController
@RequestMapping("/api/todos")
public class TodoController {
- private final TodoService todoService;
-
- @Autowired
- public TodoController(TodoService todoService) {
- this.todoService = todoService;
- }
-
- @GetMapping
- @PreAuthorize("hasRole('USER')")
- public PagedResponse getAllTodos(
- @CurrentUser UserPrincipal currentUser,
- @RequestParam(value = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
- @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size){
- return todoService.getAllTodos(currentUser, page, size);
- }
-
- @PostMapping
- @PreAuthorize("hasRole('USER')")
- public ResponseEntity> addTodo(@Valid @RequestBody Todo todo, @CurrentUser UserPrincipal currentUser){
- return todoService.addTodo(todo, currentUser);
- }
-
- @GetMapping("/{id}")
- @PreAuthorize("hasRole('USER')")
- public ResponseEntity> getTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser){
- return todoService.getTodo(id, currentUser);
- }
-
- @PutMapping("/{id}")
- @PreAuthorize("hasRole('USER')")
- public ResponseEntity> updateTodo(@PathVariable(value = "id") Long id, @Valid @RequestBody Todo newTodo, @CurrentUser UserPrincipal currentUser){
- return todoService.updateTodo(id, newTodo, currentUser);
- }
-
- @DeleteMapping("/{id}")
- @PreAuthorize("hasRole('USER')")
- public ResponseEntity> deleteTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser){
- return todoService.deleteTodo(id, currentUser);
- }
-
- @PutMapping("/{id}/complete")
- @PreAuthorize("hasRole('USER')")
- public ResponseEntity> completeTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser){
- return todoService.completeTodo(id, currentUser);
- }
-
- @PutMapping("/{id}/unComplete")
- @PreAuthorize("hasRole('USER')")
- public ResponseEntity> unCompleteTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser){
- return todoService.unCompleteTodo(id, currentUser);
- }
+ @Autowired
+ private TodoService todoService;
+
+ @GetMapping
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity> getAllTodos(
+ @CurrentUser UserPrincipal currentUser,
+ @RequestParam(value = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
+ @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) {
+
+ PagedResponse response = todoService.getAllTodos(currentUser, page, size);
+
+ return new ResponseEntity< >(response, HttpStatus.OK);
+ }
+
+ @PostMapping
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity addTodo(@Valid @RequestBody Todo todo, @CurrentUser UserPrincipal currentUser) {
+ Todo newTodo = todoService.addTodo(todo, currentUser);
+
+ return new ResponseEntity< >(newTodo, HttpStatus.CREATED);
+ }
+
+ @GetMapping("/{id}")
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity getTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) {
+ Todo todo = todoService.getTodo(id, currentUser);
+
+ return new ResponseEntity< >(todo, HttpStatus.OK);
+ }
+
+ @PutMapping("/{id}")
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity updateTodo(@PathVariable(value = "id") Long id, @Valid @RequestBody Todo newTodo,
+ @CurrentUser UserPrincipal currentUser) {
+ Todo updatedTodo = todoService.updateTodo(id, newTodo, currentUser);
+
+ return new ResponseEntity< >(updatedTodo, HttpStatus.OK);
+ }
+
+ @DeleteMapping("/{id}")
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity deleteTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) {
+ ApiResponse apiResponse = todoService.deleteTodo(id, currentUser);
+
+ return new ResponseEntity<>(apiResponse, HttpStatus.OK);
+ }
+
+ @PutMapping("/{id}/complete")
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity completeTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) {
+
+ Todo todo = todoService.completeTodo(id, currentUser);
+
+ return new ResponseEntity< >(todo, HttpStatus.OK);
+ }
+
+ @PutMapping("/{id}/unComplete")
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity unCompleteTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) {
+
+ Todo todo = todoService.unCompleteTodo(id, currentUser);
+
+ return new ResponseEntity< >(todo, HttpStatus.OK);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/controller/UserController.java b/src/main/java/com/sopromadze/blogapi/controller/UserController.java
index 1fed5c3f..f6b717e2 100644
--- a/src/main/java/com/sopromadze/blogapi/controller/UserController.java
+++ b/src/main/java/com/sopromadze/blogapi/controller/UserController.java
@@ -1,108 +1,144 @@
package com.sopromadze.blogapi.controller;
-import com.sopromadze.blogapi.model.album.Album;
-import com.sopromadze.blogapi.model.post.Post;
+import com.sopromadze.blogapi.model.Album;
+import com.sopromadze.blogapi.model.Post;
import com.sopromadze.blogapi.model.user.User;
-import com.sopromadze.blogapi.payload.*;
+import com.sopromadze.blogapi.payload.ApiResponse;
+import com.sopromadze.blogapi.payload.InfoRequest;
+import com.sopromadze.blogapi.payload.PagedResponse;
+import com.sopromadze.blogapi.payload.UserIdentityAvailability;
+import com.sopromadze.blogapi.payload.UserProfile;
+import com.sopromadze.blogapi.payload.UserSummary;
import com.sopromadze.blogapi.security.CurrentUser;
import com.sopromadze.blogapi.security.UserPrincipal;
import com.sopromadze.blogapi.service.AlbumService;
import com.sopromadze.blogapi.service.PostService;
import com.sopromadze.blogapi.service.UserService;
-import com.sopromadze.blogapi.util.AppConstants;
+import com.sopromadze.blogapi.utils.AppConstants;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping("/api/users")
public class UserController {
- private final UserService userService;
- private final PostService postService;
- private final AlbumService albumService;
-
- @Autowired
- public UserController(UserService userService, PostService postService, AlbumService albumService) {
- this.userService = userService;
- this.postService = postService;
- this.albumService = albumService;
- }
-
- @GetMapping("/me")
- @PreAuthorize("hasRole('USER')")
- public UserSummary getCurrentUser(@CurrentUser UserPrincipal currentUser){
- return userService.getCurrentUser(currentUser);
- }
-
- @GetMapping("/checkUsernameAvailability")
- public UserIdentityAvailability checkUsernameAvailability(@RequestParam(value = "username") String username){
- return userService.checkUsernameAvailability(username);
- }
-
- @GetMapping("/checkEmailAvailability")
- public UserIdentityAvailability checkEmailAvailability(@RequestParam(value = "email") String email){
- return userService.checkEmailAvailability(email);
- }
-
- @GetMapping("/{username}/profile")
- public UserProfile getUSerProfile(@PathVariable(value = "username") String username){
- return userService.getUserProfile(username);
- }
-
- @GetMapping("/{username}/posts")
- public PagedResponse getPostsCreatedBy(
- @PathVariable(value = "username") String username,
- @RequestParam(value = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
- @RequestParam(value = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size){
- return postService.getPostsCreatedBy(username, page, size);
- }
-
- @GetMapping("/{username}/albums")
- public PagedResponse getUserAlbums(
- @PathVariable(name = "username") String username,
- @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
- @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size){
-
- return albumService.getUserAlbums(username, page, size);
- }
-
- @PostMapping
- @PreAuthorize("hasRole('ADMIN')")
- public ResponseEntity> addUser(@Valid @RequestBody User user){
- return userService.addUser(user);
- }
-
- @PutMapping("/{username}")
- @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
- public ResponseEntity> updateUser(@Valid @RequestBody User newUser, @PathVariable(value = "username") String username, @CurrentUser UserPrincipal currentUser){
- return userService.updateUser(newUser, username, currentUser);
- }
-
- @DeleteMapping("/{username}")
- @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
- public ResponseEntity> deleteUser(@PathVariable(value = "username") String username, @CurrentUser UserPrincipal currentUser){
- return userService.deleteUser(username, currentUser);
- }
-
- @PutMapping("/{username}/giveAdmin")
- @PreAuthorize("hasRole('ADMIN')")
- public ResponseEntity> giveAdmin(@PathVariable(name = "username") String username){
- return userService.giveAdmin(username);
- }
-
- @PutMapping("/{username}/takeAdmin")
- @PreAuthorize("hasRole('ADMIN')")
- public ResponseEntity> takeAdmin(@PathVariable(name = "username") String username){
- return userService.takeAdmin(username);
- }
-
- @PutMapping("/setOrUpdateInfo")
- @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
- public ResponseEntity> setAddress(@CurrentUser UserPrincipal currentUser, @Valid @RequestBody InfoRequest infoRequest){
- return userService.setOrUpdateInfo(currentUser, infoRequest);
- }
+ @Autowired
+ private UserService userService;
+
+ @Autowired
+ private PostService postService;
+
+ @Autowired
+ private AlbumService albumService;
+
+ @GetMapping("/me")
+ @PreAuthorize("hasRole('USER')")
+ public ResponseEntity getCurrentUser(@CurrentUser UserPrincipal currentUser) {
+ UserSummary userSummary = userService.getCurrentUser(currentUser);
+
+ return new ResponseEntity< >(userSummary, HttpStatus.OK);
+ }
+
+ @GetMapping("/checkUsernameAvailability")
+ public ResponseEntity checkUsernameAvailability(@RequestParam(value = "username") String username) {
+ UserIdentityAvailability userIdentityAvailability = userService.checkUsernameAvailability(username);
+
+ return new ResponseEntity< >(userIdentityAvailability, HttpStatus.OK);
+ }
+
+ @GetMapping("/checkEmailAvailability")
+ public ResponseEntity checkEmailAvailability(@RequestParam(value = "email") String email) {
+ UserIdentityAvailability userIdentityAvailability = userService.checkEmailAvailability(email);
+ return new ResponseEntity< >(userIdentityAvailability, HttpStatus.OK);
+ }
+
+ @GetMapping("/{username}/profile")
+ public ResponseEntity getUSerProfile(@PathVariable(value = "username") String username) {
+ UserProfile userProfile = userService.getUserProfile(username);
+
+ return new ResponseEntity< >(userProfile, HttpStatus.OK);
+ }
+
+ @GetMapping("/{username}/posts")
+ public ResponseEntity> getPostsCreatedBy(@PathVariable(value = "username") String username,
+ @RequestParam(value = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
+ @RequestParam(value = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) {
+ PagedResponse response = postService.getPostsByCreatedBy(username, page, size);
+
+ return new ResponseEntity< >(response, HttpStatus.OK);
+ }
+
+ @GetMapping("/{username}/albums")
+ public ResponseEntity> getUserAlbums(@PathVariable(name = "username") String username,
+ @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page,
+ @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) {
+
+ PagedResponse response = albumService.getUserAlbums(username, page, size);
+
+ return new ResponseEntity< >(response, HttpStatus.OK);
+ }
+
+ @PostMapping
+ @PreAuthorize("hasRole('ADMIN')")
+ public ResponseEntity addUser(@Valid @RequestBody User user) {
+ User newUser = userService.addUser(user);
+
+ return new ResponseEntity< >(newUser, HttpStatus.CREATED);
+ }
+
+ @PutMapping("/{username}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity updateUser(@Valid @RequestBody User newUser,
+ @PathVariable(value = "username") String username, @CurrentUser UserPrincipal currentUser) {
+ User updatedUSer = userService.updateUser(newUser, username, currentUser);
+
+ return new ResponseEntity< >(updatedUSer, HttpStatus.CREATED);
+ }
+
+ @DeleteMapping("/{username}")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity deleteUser(@PathVariable(value = "username") String username,
+ @CurrentUser UserPrincipal currentUser) {
+ ApiResponse apiResponse = userService.deleteUser(username, currentUser);
+
+ return new ResponseEntity< >(apiResponse, HttpStatus.OK);
+ }
+
+ @PutMapping("/{username}/giveAdmin")
+ @PreAuthorize("hasRole('ADMIN')")
+ public ResponseEntity giveAdmin(@PathVariable(name = "username") String username) {
+ ApiResponse apiResponse = userService.giveAdmin(username);
+
+ return new ResponseEntity< >(apiResponse, HttpStatus.OK);
+ }
+
+ @PutMapping("/{username}/takeAdmin")
+ @PreAuthorize("hasRole('ADMIN')")
+ public ResponseEntity takeAdmin(@PathVariable(name = "username") String username) {
+ ApiResponse apiResponse = userService.removeAdmin(username);
+
+ return new ResponseEntity< >(apiResponse, HttpStatus.OK);
+ }
+
+ @PutMapping("/setOrUpdateInfo")
+ @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
+ public ResponseEntity setAddress(@CurrentUser UserPrincipal currentUser,
+ @Valid @RequestBody InfoRequest infoRequest) {
+ UserProfile userProfile = userService.setOrUpdateInfo(currentUser, infoRequest);
+
+ return new ResponseEntity< >(userProfile, HttpStatus.OK);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/exception/AccessDeniedException.java b/src/main/java/com/sopromadze/blogapi/exception/AccessDeniedException.java
new file mode 100644
index 00000000..ba5391e1
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/exception/AccessDeniedException.java
@@ -0,0 +1,45 @@
+package com.sopromadze.blogapi.exception;
+
+import com.sopromadze.blogapi.payload.ApiResponse;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
+public class AccessDeniedException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ private ApiResponse apiResponse;
+
+ private String message;
+
+ public AccessDeniedException(ApiResponse apiResponse) {
+ super();
+ this.apiResponse = apiResponse;
+ }
+
+ public AccessDeniedException(String message) {
+ super(message);
+ this.message = message;
+ }
+
+ public AccessDeniedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ApiResponse getApiResponse() {
+ return apiResponse;
+ }
+
+ public void setApiResponse(ApiResponse apiResponse) {
+ this.apiResponse = apiResponse;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+}
diff --git a/src/main/java/com/sopromadze/blogapi/exception/AppException.java b/src/main/java/com/sopromadze/blogapi/exception/AppException.java
index 2d98e737..a213876d 100644
--- a/src/main/java/com/sopromadze/blogapi/exception/AppException.java
+++ b/src/main/java/com/sopromadze/blogapi/exception/AppException.java
@@ -5,11 +5,13 @@
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class AppException extends RuntimeException {
- public AppException(String message) {
- super(message);
- }
+ private static final long serialVersionUID = 1L;
- public AppException(String message, Throwable cause) {
- super(message, cause);
- }
+ public AppException(String message) {
+ super(message);
+ }
+
+ public AppException(String message, Throwable cause) {
+ super(message, cause);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/exception/BadRequestException.java b/src/main/java/com/sopromadze/blogapi/exception/BadRequestException.java
index e638a1da..a35149fe 100644
--- a/src/main/java/com/sopromadze/blogapi/exception/BadRequestException.java
+++ b/src/main/java/com/sopromadze/blogapi/exception/BadRequestException.java
@@ -1,15 +1,29 @@
package com.sopromadze.blogapi.exception;
+import com.sopromadze.blogapi.payload.ApiResponse;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.BAD_REQUEST)
-public class BadRequestException extends RuntimeException{
- public BadRequestException(String message) {
- super(message);
- }
-
- public BadRequestException(String message, Throwable cause) {
- super(message, cause);
- }
+public class BadRequestException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ private ApiResponse apiResponse;
+
+ public BadRequestException(ApiResponse apiResponse) {
+ super();
+ this.apiResponse = apiResponse;
+ }
+
+ public BadRequestException(String message) {
+ super(message);
+ }
+
+ public BadRequestException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ApiResponse getApiResponse() {
+ return apiResponse;
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/exception/BlogapiException.java b/src/main/java/com/sopromadze/blogapi/exception/BlogapiException.java
new file mode 100644
index 00000000..4da4fd63
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/exception/BlogapiException.java
@@ -0,0 +1,32 @@
+package com.sopromadze.blogapi.exception;
+
+import org.springframework.http.HttpStatus;
+
+public class BlogapiException extends RuntimeException {
+
+ private static final long serialVersionUID = -6593330219878485669L;
+
+ private final HttpStatus status;
+ private final String message;
+
+ public BlogapiException(HttpStatus status, String message) {
+ super();
+ this.status = status;
+ this.message = message;
+ }
+
+ public BlogapiException(HttpStatus status, String message, Throwable exception) {
+ super(exception);
+ this.status = status;
+ this.message = message;
+ }
+
+ public HttpStatus getStatus() {
+ return status;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+}
diff --git a/src/main/java/com/sopromadze/blogapi/exception/ResourceNotFoundException.java b/src/main/java/com/sopromadze/blogapi/exception/ResourceNotFoundException.java
index de5cc830..4dd90e07 100644
--- a/src/main/java/com/sopromadze/blogapi/exception/ResourceNotFoundException.java
+++ b/src/main/java/com/sopromadze/blogapi/exception/ResourceNotFoundException.java
@@ -1,30 +1,45 @@
package com.sopromadze.blogapi.exception;
+import com.sopromadze.blogapi.payload.ApiResponse;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
-public class ResourceNotFoundException extends RuntimeException{
- private String resourceName;
- private String fieldName;
- private Object fieldValue;
-
- public ResourceNotFoundException(String resourceName, String fieldName, Object fieldValue) {
- super(String.format("%s not found with %s: '%s'", resourceName, fieldName, fieldValue));
- this.resourceName = resourceName;
- this.fieldName = fieldName;
- this.fieldValue = fieldValue;
- }
-
- public String getResourceName() {
- return resourceName;
- }
-
- public String getFieldName() {
- return fieldName;
- }
-
- public Object getFieldValue() {
- return fieldValue;
- }
+public class ResourceNotFoundException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ private transient ApiResponse apiResponse;
+
+ private String resourceName;
+ private String fieldName;
+ private Object fieldValue;
+
+ public ResourceNotFoundException(String resourceName, String fieldName, Object fieldValue) {
+ super();
+ this.resourceName = resourceName;
+ this.fieldName = fieldName;
+ this.fieldValue = fieldValue;
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ public Object getFieldValue() {
+ return fieldValue;
+ }
+
+ public ApiResponse getApiResponse() {
+ return apiResponse;
+ }
+
+ private void setApiResponse() {
+ String message = String.format("%s not found with %s: '%s'", resourceName, fieldName, fieldValue);
+
+ apiResponse = new ApiResponse(Boolean.FALSE, message);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/exception/ResponseEntityErrorException.java b/src/main/java/com/sopromadze/blogapi/exception/ResponseEntityErrorException.java
new file mode 100644
index 00000000..f5aeaaed
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/exception/ResponseEntityErrorException.java
@@ -0,0 +1,18 @@
+package com.sopromadze.blogapi.exception;
+
+import com.sopromadze.blogapi.payload.ApiResponse;
+import org.springframework.http.ResponseEntity;
+
+public class ResponseEntityErrorException extends RuntimeException {
+ private static final long serialVersionUID = -3156815846745801694L;
+
+ private transient ResponseEntity apiResponse;
+
+ public ResponseEntityErrorException(ResponseEntity apiResponse) {
+ this.apiResponse = apiResponse;
+ }
+
+ public ResponseEntity getApiResponse() {
+ return apiResponse;
+ }
+}
diff --git a/src/main/java/com/sopromadze/blogapi/exception/RestControllerExceptionHandler.java b/src/main/java/com/sopromadze/blogapi/exception/RestControllerExceptionHandler.java
index ee0634d5..a9372e45 100644
--- a/src/main/java/com/sopromadze/blogapi/exception/RestControllerExceptionHandler.java
+++ b/src/main/java/com/sopromadze/blogapi/exception/RestControllerExceptionHandler.java
@@ -1,10 +1,10 @@
package com.sopromadze.blogapi.exception;
+import com.sopromadze.blogapi.payload.ApiResponse;
import com.sopromadze.blogapi.payload.ExceptionResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
-import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
@@ -21,45 +21,98 @@
@ControllerAdvice
public class RestControllerExceptionHandler {
- @ExceptionHandler({MethodArgumentNotValidException.class})
- @ResponseBody
- @ResponseStatus(HttpStatus.BAD_REQUEST)
- public ResponseEntity> resolveException(MethodArgumentNotValidException ex){
- List fieldErrors = ex.getBindingResult().getFieldErrors();
- List messages = new ArrayList<>();
- for (FieldError error : fieldErrors){
- messages.add(error.getField() + " - " + error.getDefaultMessage());
- }
- return new ResponseEntity<>(new ExceptionResponse(messages, HttpStatus.BAD_REQUEST.getReasonPhrase(), HttpStatus.BAD_REQUEST.value()), HttpStatus.BAD_REQUEST);
- }
-
- @ExceptionHandler({MethodArgumentTypeMismatchException.class})
- @ResponseBody
- @ResponseStatus(HttpStatus.BAD_REQUEST)
- public ResponseEntity> resolveException(MethodArgumentTypeMismatchException ex){
- String message = "Parameter '" + ex.getParameter().getParameterName() + "' must be '" + Objects.requireNonNull(ex.getRequiredType()).getSimpleName() + "'";
- List messages = new ArrayList<>();
- messages.add(message);
- return new ResponseEntity<>(new ExceptionResponse(messages, HttpStatus.BAD_REQUEST.getReasonPhrase(), HttpStatus.BAD_REQUEST.value()), HttpStatus.BAD_REQUEST);
- }
-
- @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
- @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
- @ResponseBody
- public ResponseEntity> resolveException(HttpRequestMethodNotSupportedException ex){
- String message = "Request method '" + ex.getMethod() +"' not supported. List of all supported methods - " + ex.getSupportedHttpMethods();
- List messages = new ArrayList<>();
- messages.add(message);
- return new ResponseEntity<>(new ExceptionResponse(messages, HttpStatus.METHOD_NOT_ALLOWED.getReasonPhrase(), HttpStatus.METHOD_NOT_ALLOWED.value()), HttpStatus.METHOD_NOT_ALLOWED);
- }
-
- @ExceptionHandler({HttpMessageNotReadableException.class})
- @ResponseBody
- @ResponseStatus(HttpStatus.BAD_REQUEST)
- public ResponseEntity> resolveException(HttpMessageNotReadableException ex){
- String message = "Please provide Request Body in valid JSON format";
- List messages = new ArrayList<>();
- messages.add(message);
- return new ResponseEntity<>(new ExceptionResponse(messages, HttpStatus.BAD_REQUEST.getReasonPhrase(), HttpStatus.BAD_REQUEST.value()), HttpStatus.BAD_REQUEST);
- }
+ public ResponseEntity resolveException(BlogapiException exception) {
+ String message = exception.getMessage();
+ HttpStatus status = exception.getStatus();
+
+ ApiResponse apiResponse = new ApiResponse();
+
+ apiResponse.setSuccess(Boolean.FALSE);
+ apiResponse.setMessage(message);
+
+ return new ResponseEntity<>(apiResponse, status);
+ }
+
+ @ExceptionHandler(UnauthorizedException.class)
+ @ResponseBody
+ @ResponseStatus(code = HttpStatus.UNAUTHORIZED)
+ public ResponseEntity resolveException(UnauthorizedException exception) {
+
+ ApiResponse apiResponse = exception.getApiResponse();
+
+ return new ResponseEntity<>(apiResponse, HttpStatus.UNAUTHORIZED);
+ }
+
+ @ExceptionHandler(BadRequestException.class)
+ @ResponseBody
+ public ResponseEntity resolveException(BadRequestException exception) {
+ ApiResponse apiResponse = exception.getApiResponse();
+
+ return new ResponseEntity<>(apiResponse, HttpStatus.BAD_REQUEST);
+ }
+
+ @ExceptionHandler(ResourceNotFoundException.class)
+ @ResponseBody
+ public ResponseEntity resolveException(ResourceNotFoundException exception) {
+ ApiResponse apiResponse = exception.getApiResponse();
+
+ return new ResponseEntity<>(apiResponse, HttpStatus.NOT_FOUND);
+ }
+
+ @ExceptionHandler(AccessDeniedException.class)
+ @ResponseBody
+ public ResponseEntity resolveException(AccessDeniedException exception) {
+ ApiResponse apiResponse = exception.getApiResponse();
+
+ return new ResponseEntity< >(apiResponse, HttpStatus.FORBIDDEN);
+ }
+
+ @ExceptionHandler({ MethodArgumentNotValidException.class })
+ @ResponseBody
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public ResponseEntity resolveException(MethodArgumentNotValidException ex) {
+ List fieldErrors = ex.getBindingResult().getFieldErrors();
+ List messages = new ArrayList<>(fieldErrors.size());
+ for (FieldError error : fieldErrors) {
+ messages.add(error.getField() + " - " + error.getDefaultMessage());
+ }
+ return new ResponseEntity<>(new ExceptionResponse(messages, HttpStatus.BAD_REQUEST.getReasonPhrase(),
+ HttpStatus.BAD_REQUEST.value()), HttpStatus.BAD_REQUEST);
+ }
+
+ @ExceptionHandler({ MethodArgumentTypeMismatchException.class })
+ @ResponseBody
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public ResponseEntity resolveException(MethodArgumentTypeMismatchException ex) {
+ String message = "Parameter '" + ex.getParameter().getParameterName() + "' must be '"
+ + Objects.requireNonNull(ex.getRequiredType()).getSimpleName() + "'";
+ List messages = new ArrayList<>(1);
+ messages.add(message);
+ return new ResponseEntity<>(new ExceptionResponse(messages, HttpStatus.BAD_REQUEST.getReasonPhrase(),
+ HttpStatus.BAD_REQUEST.value()), HttpStatus.BAD_REQUEST);
+ }
+
+ @ExceptionHandler({ HttpRequestMethodNotSupportedException.class })
+ @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
+ @ResponseBody
+ public ResponseEntity resolveException(HttpRequestMethodNotSupportedException ex) {
+ String message = "Request method '" + ex.getMethod() + "' not supported. List of all supported methods - "
+ + ex.getSupportedHttpMethods();
+ List messages = new ArrayList<>(1);
+ messages.add(message);
+
+ return new ResponseEntity<>(new ExceptionResponse(messages, HttpStatus.METHOD_NOT_ALLOWED.getReasonPhrase(),
+ HttpStatus.METHOD_NOT_ALLOWED.value()), HttpStatus.METHOD_NOT_ALLOWED);
+ }
+
+ @ExceptionHandler({ HttpMessageNotReadableException.class })
+ @ResponseBody
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public ResponseEntity resolveException(HttpMessageNotReadableException ex) {
+ String message = "Please provide Request Body in valid JSON format";
+ List messages = new ArrayList<>(1);
+ messages.add(message);
+ return new ResponseEntity<>(new ExceptionResponse(messages, HttpStatus.BAD_REQUEST.getReasonPhrase(),
+ HttpStatus.BAD_REQUEST.value()), HttpStatus.BAD_REQUEST);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/exception/UnauthorizedException.java b/src/main/java/com/sopromadze/blogapi/exception/UnauthorizedException.java
new file mode 100644
index 00000000..1760c66b
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/exception/UnauthorizedException.java
@@ -0,0 +1,45 @@
+package com.sopromadze.blogapi.exception;
+
+import com.sopromadze.blogapi.payload.ApiResponse;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
+public class UnauthorizedException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ private ApiResponse apiResponse;
+
+ private String message;
+
+ public UnauthorizedException(ApiResponse apiResponse) {
+ super();
+ this.apiResponse = apiResponse;
+ }
+
+ public UnauthorizedException(String message) {
+ super(message);
+ this.message = message;
+ }
+
+ public UnauthorizedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ApiResponse getApiResponse() {
+ return apiResponse;
+ }
+
+ public void setApiResponse(ApiResponse apiResponse) {
+ this.apiResponse = apiResponse;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+}
diff --git a/src/main/java/com/sopromadze/blogapi/model/Album.java b/src/main/java/com/sopromadze/blogapi/model/Album.java
new file mode 100644
index 00000000..311d5991
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/model/Album.java
@@ -0,0 +1,64 @@
+package com.sopromadze.blogapi.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.sopromadze.blogapi.model.audit.UserDateAudit;
+import com.sopromadze.blogapi.model.user.User;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.validation.constraints.NotBlank;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Entity
+@Data
+@Table(name = "albums", uniqueConstraints = { @UniqueConstraint(columnNames = { "title" }) })
+public class Album extends UserDateAudit {
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NotBlank
+ @Column(name = "title")
+ private String title;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id")
+ private User user;
+
+ @OneToMany(mappedBy = "album", cascade = CascadeType.ALL, orphanRemoval = true)
+ private List photo;
+
+ @JsonIgnore
+ public User getUser() {
+ return user;
+ }
+
+ public List getPhoto() {
+ return this.photo == null ? null : new ArrayList<>(this.photo);
+ }
+
+ public void setPhoto(List photo) {
+ if (photo == null) {
+ this.photo = null;
+ } else {
+ this.photo = Collections.unmodifiableList(photo);
+ }
+ }
+}
diff --git a/src/main/java/com/sopromadze/blogapi/model/Category.java b/src/main/java/com/sopromadze/blogapi/model/Category.java
new file mode 100644
index 00000000..102c6e39
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/model/Category.java
@@ -0,0 +1,58 @@
+package com.sopromadze.blogapi.model;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+import com.sopromadze.blogapi.model.audit.UserDateAudit;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Entity
+@Data
+@NoArgsConstructor
+@Table(name = "categories")
+@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
+public class Category extends UserDateAudit {
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "name")
+ private String name;
+
+ @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true)
+ private List posts;
+
+ public Category(String name) {
+ super();
+ this.name = name;
+ }
+
+ public List getPosts() {
+ return this.posts == null ? null : new ArrayList<>(this.posts);
+ }
+
+ public void setPosts(List posts) {
+ if (posts == null) {
+ this.posts = null;
+ } else {
+ this.posts = Collections.unmodifiableList(posts);
+ }
+ }
+
+}
diff --git a/src/main/java/com/sopromadze/blogapi/model/comment/Comment.java b/src/main/java/com/sopromadze/blogapi/model/Comment.java
similarity index 61%
rename from src/main/java/com/sopromadze/blogapi/model/comment/Comment.java
rename to src/main/java/com/sopromadze/blogapi/model/Comment.java
index 2080b830..d9d21972 100644
--- a/src/main/java/com/sopromadze/blogapi/model/comment/Comment.java
+++ b/src/main/java/com/sopromadze/blogapi/model/Comment.java
@@ -1,18 +1,33 @@
-package com.sopromadze.blogapi.model.comment;
+package com.sopromadze.blogapi.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.sopromadze.blogapi.model.audit.UserDateAudit;
-import com.sopromadze.blogapi.model.post.Post;
import com.sopromadze.blogapi.model.user.User;
-
-import javax.persistence.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
+@EqualsAndHashCode(callSuper = true)
@Entity
+@Data
+@NoArgsConstructor
@Table(name = "comments")
public class Comment extends UserDateAudit {
+ private static final long serialVersionUID = 1L;
+
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@@ -41,61 +56,17 @@ public class Comment extends UserDateAudit {
@JoinColumn(name = "user_id")
private User user;
- public Comment(){
-
- }
-
public Comment(@NotBlank @Size(min = 10, message = "Comment body must be minimum 10 characters") String body) {
this.body = body;
}
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getEmail() {
- return email;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public String getBody() {
- return body;
- }
-
- public void setBody(String body) {
- this.body = body;
- }
-
@JsonIgnore
public Post getPost() {
return post;
}
- public void setPost(Post post) {
- this.post = post;
- }
-
@JsonIgnore
public User getUser() {
return user;
}
-
- public void setUser(User user) {
- this.user = user;
- }
}
diff --git a/src/main/java/com/sopromadze/blogapi/model/Photo.java b/src/main/java/com/sopromadze/blogapi/model/Photo.java
new file mode 100644
index 00000000..7aaa1d2a
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/model/Photo.java
@@ -0,0 +1,60 @@
+package com.sopromadze.blogapi.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.sopromadze.blogapi.model.audit.UserDateAudit;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.validation.constraints.NotBlank;
+
+@EqualsAndHashCode(callSuper = true)
+@Entity
+@Data
+@NoArgsConstructor
+@Table(name = "photos", uniqueConstraints = { @UniqueConstraint(columnNames = { "title" }) })
+public class Photo extends UserDateAudit {
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NotBlank
+ @Column(name = "title")
+ private String title;
+
+ @NotBlank
+ @Column(name = "url")
+ private String url;
+
+ @NotBlank
+ @Column(name = "thumbnail_url")
+ private String thumbnailUrl;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "album_id")
+ private Album album;
+
+ public Photo(@NotBlank String title, @NotBlank String url, @NotBlank String thumbnailUrl, Album album) {
+ this.title = title;
+ this.url = url;
+ this.thumbnailUrl = thumbnailUrl;
+ this.album = album;
+ }
+
+ @JsonIgnore
+ public Album getAlbum() {
+ return album;
+ }
+}
diff --git a/src/main/java/com/sopromadze/blogapi/model/Post.java b/src/main/java/com/sopromadze/blogapi/model/Post.java
new file mode 100644
index 00000000..6b97dbcf
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/model/Post.java
@@ -0,0 +1,95 @@
+package com.sopromadze.blogapi.model;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+import com.sopromadze.blogapi.model.audit.UserDateAudit;
+import com.sopromadze.blogapi.model.user.User;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Entity
+@Data
+@Table(name = "posts", uniqueConstraints = { @UniqueConstraint(columnNames = { "title" }) })
+@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
+public class Post extends UserDateAudit {
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "title")
+ private String title;
+
+ @Column(name = "body")
+ private String body;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id")
+ private User user;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "category_id")
+ private Category category;
+
+ @JsonIgnore
+ @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
+ private List comments;
+
+ @ManyToMany(fetch = FetchType.LAZY)
+ @JoinTable(name = "post_tag", joinColumns = @JoinColumn(name = "post_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "tag_id", referencedColumnName = "id"))
+ private List tags;
+
+ @JsonIgnore
+ public User getUser() {
+ return user;
+ }
+
+ public void setUser(User user) {
+ this.user = user;
+ }
+
+ public List getComments() {
+ return comments == null ? null : new ArrayList<>(comments);
+ }
+
+ public void setComments(List comments) {
+ if (comments == null) {
+ this.comments = null;
+ } else {
+ this.comments = Collections.unmodifiableList(comments);
+ }
+ }
+
+ public List getTags() {
+ return tags == null ? null : new ArrayList<>(tags);
+ }
+
+ public void setTags(List tags) {
+ if (tags == null) {
+ this.tags = null;
+ } else {
+ this.tags = Collections.unmodifiableList(tags);
+ }
+ }
+}
diff --git a/src/main/java/com/sopromadze/blogapi/model/Tag.java b/src/main/java/com/sopromadze/blogapi/model/Tag.java
new file mode 100644
index 00000000..a3cbd193
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/model/Tag.java
@@ -0,0 +1,63 @@
+package com.sopromadze.blogapi.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.sopromadze.blogapi.model.audit.UserDateAudit;
+import com.sopromadze.blogapi.model.Post;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.Table;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Entity
+@Data
+@NoArgsConstructor
+@Table(name = "tags")
+//@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
+public class Tag extends UserDateAudit {
+
+ private static final long serialVersionUID = -5298707266367331514L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "name")
+ private String name;
+
+ @JsonIgnore
+ @ManyToMany(fetch = FetchType.EAGER)
+ @JoinTable(name = "post_tag", joinColumns = @JoinColumn(name = "tag_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "post_id", referencedColumnName = "id"))
+ private List posts;
+
+ public Tag(String name) {
+ super();
+ this.name = name;
+ }
+
+ public List getPosts() {
+ return posts == null ? null : new ArrayList<>(posts);
+ }
+
+ public void setPosts(List posts) {
+ if (posts == null) {
+ this.posts = null;
+ } else {
+ this.posts = Collections.unmodifiableList(posts);
+ }
+ }
+
+}
diff --git a/src/main/java/com/sopromadze/blogapi/model/Todo.java b/src/main/java/com/sopromadze/blogapi/model/Todo.java
new file mode 100644
index 00000000..7fed93d1
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/model/Todo.java
@@ -0,0 +1,48 @@
+package com.sopromadze.blogapi.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.sopromadze.blogapi.model.audit.UserDateAudit;
+import com.sopromadze.blogapi.model.user.User;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.validation.constraints.NotBlank;
+
+@EqualsAndHashCode(callSuper = true)
+@Entity
+@Data
+@Table(name = "todos", uniqueConstraints = { @UniqueConstraint(columnNames = { "title" }) })
+public class Todo extends UserDateAudit {
+
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NotBlank
+ @Column(name = "title")
+ private String title;
+
+ @Column(name = "completed")
+ private Boolean completed;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id")
+ private User user;
+
+ @JsonIgnore
+ public User getUser() {
+ return user;
+ }
+}
diff --git a/src/main/java/com/sopromadze/blogapi/model/album/Album.java b/src/main/java/com/sopromadze/blogapi/model/album/Album.java
deleted file mode 100644
index 23319d41..00000000
--- a/src/main/java/com/sopromadze/blogapi/model/album/Album.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.sopromadze.blogapi.model.album;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.sopromadze.blogapi.model.audit.UserDateAudit;
-import com.sopromadze.blogapi.model.photo.Photo;
-import com.sopromadze.blogapi.model.user.User;
-
-import javax.persistence.*;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.Size;
-import java.util.List;
-
-@Entity
-@Table(name = "albums", uniqueConstraints = {
- @UniqueConstraint(columnNames = {
- "title"
- })
-})
-public class Album extends UserDateAudit {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @NotBlank
- @Column(name = "title")
- private String title;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_id")
- private User user;
-
- @OneToMany(mappedBy = "album", cascade = CascadeType.ALL, orphanRemoval = true)
- private List photo;
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- @JsonIgnore
- public User getUser() {
- return user;
- }
-
- public void setUser(User user) {
- this.user = user;
- }
-}
diff --git a/src/main/java/com/sopromadze/blogapi/model/audit/DateAudit.java b/src/main/java/com/sopromadze/blogapi/model/audit/DateAudit.java
index 95ae07aa..c9f8abe7 100644
--- a/src/main/java/com/sopromadze/blogapi/model/audit/DateAudit.java
+++ b/src/main/java/com/sopromadze/blogapi/model/audit/DateAudit.java
@@ -1,6 +1,7 @@
package com.sopromadze.blogapi.model.audit;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@@ -12,34 +13,22 @@
import java.time.Instant;
@MappedSuperclass
+@Data
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(
- value = {"createdAt", "updatedAt"},
- allowGetters = true
+ value = { "createdAt", "updatedAt" },
+ allowGetters = true
)
public abstract class DateAudit implements Serializable {
- @CreatedDate
- @Column(nullable = false, updatable = false)
- private Instant createdAt;
+ private static final long serialVersionUID = 1L;
- @LastModifiedDate
- @Column(nullable = false)
- private Instant updatedAt;
+ @CreatedDate
+ @Column(nullable = false, updatable = false)
+ private Instant createdAt;
- public Instant getCreatedAt() {
- return createdAt;
- }
+ @LastModifiedDate
+ @Column(nullable = false)
+ private Instant updatedAt;
- public void setCreatedAt(Instant createdAt) {
- this.createdAt = createdAt;
- }
-
- public Instant getUpdatedAt() {
- return updatedAt;
- }
-
- public void setUpdatedAt(Instant updatedAt) {
- this.updatedAt = updatedAt;
- }
}
diff --git a/src/main/java/com/sopromadze/blogapi/model/audit/UserDateAudit.java b/src/main/java/com/sopromadze/blogapi/model/audit/UserDateAudit.java
index 525f15a1..10644bd5 100644
--- a/src/main/java/com/sopromadze/blogapi/model/audit/UserDateAudit.java
+++ b/src/main/java/com/sopromadze/blogapi/model/audit/UserDateAudit.java
@@ -1,38 +1,28 @@
package com.sopromadze.blogapi.model.audit;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.LastModifiedBy;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
+@EqualsAndHashCode(callSuper = true)
@MappedSuperclass
+@Data
@JsonIgnoreProperties(
- value = {"createdBY", "updatedBy"},
- allowGetters = true
+ value = { "createdBY", "updatedBy" },
+ allowGetters = true
)
-public abstract class UserDateAudit extends DateAudit{
- @CreatedBy
- @Column(updatable = false)
- private Long createdBy;
+public abstract class UserDateAudit extends DateAudit {
+ private static final long serialVersionUID = 1L;
- @LastModifiedBy
- private Long updatedBy;
+ @CreatedBy
+ @Column(updatable = false)
+ private Long createdBy;
- public Long getCreatedBy() {
- return createdBy;
- }
-
- public void setCreatedBy(Long createdBy) {
- this.createdBy = createdBy;
- }
-
- public Long getUpdatedBy() {
- return updatedBy;
- }
-
- public void setUpdatedBy(Long updatedBy) {
- this.updatedBy = updatedBy;
- }
+ @LastModifiedBy
+ private Long updatedBy;
}
diff --git a/src/main/java/com/sopromadze/blogapi/model/photo/Photo.java b/src/main/java/com/sopromadze/blogapi/model/photo/Photo.java
deleted file mode 100644
index 2e106891..00000000
--- a/src/main/java/com/sopromadze/blogapi/model/photo/Photo.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.sopromadze.blogapi.model.photo;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.sopromadze.blogapi.model.album.Album;
-import com.sopromadze.blogapi.model.audit.UserDateAudit;
-
-import javax.persistence.*;
-import javax.validation.constraints.NotBlank;
-
-@Entity
-@Table(name = "photos", uniqueConstraints = {
- @UniqueConstraint(columnNames = {
- "title"
- })
-})
-public class Photo extends UserDateAudit {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @NotBlank
- @Column(name = "title")
- private String title;
-
- @NotBlank
- @Column(name = "url")
- private String url;
-
- @NotBlank
- @Column(name = "thumbnail_url")
- private String thumbnailUrl;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "album_id")
- private Album album;
-
- public Photo(){
-
- }
-
- public Photo(@NotBlank String title, @NotBlank String url, @NotBlank String thumbnailUrl, Album album) {
- this.title = title;
- this.url = url;
- this.thumbnailUrl = thumbnailUrl;
- this.album = album;
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getUrl() {
- return url;
- }
-
- public void setUrl(String url) {
- this.url = url;
- }
-
- public String getThumbnailUrl() {
- return thumbnailUrl;
- }
-
- public void setThumbnailUrl(String thumbnailUrl) {
- this.thumbnailUrl = thumbnailUrl;
- }
-
- @JsonIgnore
- public Album getAlbum() {
- return album;
- }
-
- public void setAlbum(Album album) {
- this.album = album;
- }
-}
diff --git a/src/main/java/com/sopromadze/blogapi/model/post/Post.java b/src/main/java/com/sopromadze/blogapi/model/post/Post.java
deleted file mode 100644
index 977b90f3..00000000
--- a/src/main/java/com/sopromadze/blogapi/model/post/Post.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.sopromadze.blogapi.model.post;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.sopromadze.blogapi.model.audit.UserDateAudit;
-import com.sopromadze.blogapi.model.comment.Comment;
-import com.sopromadze.blogapi.model.user.User;
-
-import javax.persistence.*;
-import java.util.List;
-
-@Entity
-@Table(name = "posts", uniqueConstraints = {
- @UniqueConstraint(columnNames = {
- "title"
- })
-})
-public class Post extends UserDateAudit {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @Column(name = "title")
- private String title;
-
- @Column(name = "body")
- private String body;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_id")
- private User user;
-
- @JsonIgnore
- @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
- private List comments;
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getBody() {
- return body;
- }
-
- public void setBody(String body) {
- this.body = body;
- }
-
- @JsonIgnore
- public User getUser() {
- return user;
- }
-
- public void setUser(User user) {
- this.user = user;
- }
-
- public List getComments() {
- return comments;
- }
-
- public void setComments(List comments) {
- this.comments = comments;
- }
-}
diff --git a/src/main/java/com/sopromadze/blogapi/model/role/Role.java b/src/main/java/com/sopromadze/blogapi/model/role/Role.java
index dd4b716d..00231ca9 100644
--- a/src/main/java/com/sopromadze/blogapi/model/role/Role.java
+++ b/src/main/java/com/sopromadze/blogapi/model/role/Role.java
@@ -1,42 +1,33 @@
package com.sopromadze.blogapi.model.role;
+import lombok.Data;
+import lombok.NoArgsConstructor;
import org.hibernate.annotations.NaturalId;
-import javax.persistence.*;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
@Entity
+@Data
+@NoArgsConstructor
@Table(name = "roles")
public class Role {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @Enumerated(EnumType.STRING)
- @NaturalId
- @Column(name = "name")
- private RoleName name;
-
- public Role(){
-
- }
-
- public Role(RoleName name) {
- this.name = name;
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public RoleName getName() {
- return name;
- }
-
- public void setName(RoleName name) {
- this.name = name;
- }
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Enumerated(EnumType.STRING)
+ @NaturalId
+ @Column(name = "name")
+ private RoleName name;
+
+ public Role(RoleName name) {
+ this.name = name;
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/model/role/RoleName.java b/src/main/java/com/sopromadze/blogapi/model/role/RoleName.java
index ae05926c..f2064b2f 100644
--- a/src/main/java/com/sopromadze/blogapi/model/role/RoleName.java
+++ b/src/main/java/com/sopromadze/blogapi/model/role/RoleName.java
@@ -1,6 +1,6 @@
package com.sopromadze.blogapi.model.role;
public enum RoleName {
- ROLE_ADMIN,
- ROLE_USER,
+ ROLE_ADMIN,
+ ROLE_USER,
}
diff --git a/src/main/java/com/sopromadze/blogapi/model/todo/Todo.java b/src/main/java/com/sopromadze/blogapi/model/todo/Todo.java
deleted file mode 100644
index c23652fd..00000000
--- a/src/main/java/com/sopromadze/blogapi/model/todo/Todo.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.sopromadze.blogapi.model.todo;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.sopromadze.blogapi.model.audit.UserDateAudit;
-import com.sopromadze.blogapi.model.user.User;
-
-import javax.persistence.*;
-import javax.validation.constraints.NotBlank;
-
-@Entity
-@Table(name = "todos", uniqueConstraints = {
- @UniqueConstraint(columnNames = {
- "title"
- })
-})
-public class Todo extends UserDateAudit {
-
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @NotBlank
- @Column(name = "title")
- private String title;
-
- @Column(name = "completed")
- private Boolean completed;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_id")
- private User user;
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public Boolean getCompleted() {
- return completed;
- }
-
- public void setCompleted(Boolean completed) {
- this.completed = completed;
- }
-
- @JsonIgnore
- public User getUser() {
- return user;
- }
-
- public void setUser(User user) {
- this.user = user;
- }
-}
diff --git a/src/main/java/com/sopromadze/blogapi/model/user/Address.java b/src/main/java/com/sopromadze/blogapi/model/user/Address.java
index 8e97eca0..f686bbbf 100644
--- a/src/main/java/com/sopromadze/blogapi/model/user/Address.java
+++ b/src/main/java/com/sopromadze/blogapi/model/user/Address.java
@@ -2,142 +2,111 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.sopromadze.blogapi.model.audit.UserDateAudit;
-
-import javax.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
import java.time.Instant;
+@EqualsAndHashCode(callSuper = true)
@Entity
+@Data
+@NoArgsConstructor
@Table(name = "address")
public class Address extends UserDateAudit {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @Column(name = "street")
- private String street;
-
- @Column(name = "suite")
- private String suite;
-
- @Column(name = "city")
- private String city;
-
- @Column(name = "zipcode")
- private String zipcode;
-
- @OneToOne(cascade = CascadeType.ALL)
- @JoinColumn(name = "geo_id")
- private Geo geo;
-
- @OneToOne(mappedBy = "address")
- private User user;
-
- public Address(){
-
- }
-
- public Address(String street, String suite, String city, String zipcode, Geo geo) {
- this.street = street;
- this.suite = suite;
- this.city = city;
- this.zipcode = zipcode;
- this.geo = geo;
- }
-
- public String getStreet() {
- return street;
- }
-
- public void setStreet(String street) {
- this.street = street;
- }
-
- public String getSuite() {
- return suite;
- }
-
- public void setSuite(String suite) {
- this.suite = suite;
- }
-
- public String getCity() {
- return city;
- }
-
- public void setCity(String city) {
- this.city = city;
- }
-
- public String getZipcode() {
- return zipcode;
- }
-
- public void setZipcode(String zipcode) {
- this.zipcode = zipcode;
- }
-
- @JsonIgnore
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public Geo getGeo() {
- return geo;
- }
-
- public void setGeo(Geo geo) {
- this.geo = geo;
- }
-
- @JsonIgnore
- @Override
- public Long getCreatedBy() {
- return super.getCreatedBy();
- }
-
- @JsonIgnore
- @Override
- public void setCreatedBy(Long createdBy) {
- super.setCreatedBy(createdBy);
- }
-
- @JsonIgnore
- @Override
- public Long getUpdatedBy() {
- return super.getUpdatedBy();
- }
-
- @JsonIgnore
- @Override
- public void setUpdatedBy(Long updatedBy) {
- super.setUpdatedBy(updatedBy);
- }
-
- @JsonIgnore
- @Override
- public Instant getCreatedAt() {
- return super.getCreatedAt();
- }
-
- @JsonIgnore
- @Override
- public void setCreatedAt(Instant createdAt) {
- super.setCreatedAt(createdAt);
- }
-
- @JsonIgnore
- @Override
- public Instant getUpdatedAt() {
- return super.getUpdatedAt();
- }
-
- @JsonIgnore
- @Override
- public void setUpdatedAt(Instant updatedAt) {
- super.setUpdatedAt(updatedAt);
- }
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "street")
+ private String street;
+
+ @Column(name = "suite")
+ private String suite;
+
+ @Column(name = "city")
+ private String city;
+
+ @Column(name = "zipcode")
+ private String zipcode;
+
+ @OneToOne(cascade = CascadeType.ALL)
+ @JoinColumn(name = "geo_id")
+ private Geo geo;
+
+ @OneToOne(mappedBy = "address")
+ private User user;
+
+ public Address(String street, String suite, String city, String zipcode, Geo geo) {
+ this.street = street;
+ this.suite = suite;
+ this.city = city;
+ this.zipcode = zipcode;
+ this.geo = geo;
+ }
+
+ @JsonIgnore
+ public Long getId() {
+ return id;
+ }
+
+ @JsonIgnore
+ @Override
+ public Long getCreatedBy() {
+ return super.getCreatedBy();
+ }
+
+ @JsonIgnore
+ @Override
+ public void setCreatedBy(Long createdBy) {
+ super.setCreatedBy(createdBy);
+ }
+
+ @JsonIgnore
+ @Override
+ public Long getUpdatedBy() {
+ return super.getUpdatedBy();
+ }
+
+ @JsonIgnore
+ @Override
+ public void setUpdatedBy(Long updatedBy) {
+ super.setUpdatedBy(updatedBy);
+ }
+
+ @JsonIgnore
+ @Override
+ public Instant getCreatedAt() {
+ return super.getCreatedAt();
+ }
+
+ @JsonIgnore
+ @Override
+ public void setCreatedAt(Instant createdAt) {
+ super.setCreatedAt(createdAt);
+ }
+
+ @JsonIgnore
+ @Override
+ public Instant getUpdatedAt() {
+ return super.getUpdatedAt();
+ }
+
+ @JsonIgnore
+ @Override
+ public void setUpdatedAt(Instant updatedAt) {
+ super.setUpdatedAt(updatedAt);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/model/user/Company.java b/src/main/java/com/sopromadze/blogapi/model/user/Company.java
index 932e9660..7cc93a2f 100644
--- a/src/main/java/com/sopromadze/blogapi/model/user/Company.java
+++ b/src/main/java/com/sopromadze/blogapi/model/user/Company.java
@@ -2,117 +2,104 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.sopromadze.blogapi.model.audit.UserDateAudit;
-
-import javax.persistence.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
import java.time.Instant;
+@EqualsAndHashCode(callSuper = true)
@Entity
+@Data
+@NoArgsConstructor
@Table(name = "company")
public class Company extends UserDateAudit {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @Column(name = "name")
- private String name;
-
- @Column(name = "catch_phrase")
- private String catchPhrase;
-
- @Column(name = "bs")
- private String bs;
-
- @OneToOne(mappedBy = "company")
- private User user;
-
- public Company(){
-
- }
-
- public Company(String name, String catchPhrase, String bs) {
- this.name = name;
- this.catchPhrase = catchPhrase;
- this.bs = bs;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getCatchPhrase() {
- return catchPhrase;
- }
-
- public void setCatchPhrase(String catchPhrase) {
- this.catchPhrase = catchPhrase;
- }
-
- public String getBs() {
- return bs;
- }
-
- public void setBs(String bs) {
- this.bs = bs;
- }
-
- @JsonIgnore
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- @JsonIgnore
- @Override
- public Long getCreatedBy() {
- return super.getCreatedBy();
- }
-
- @JsonIgnore
- @Override
- public void setCreatedBy(Long createdBy) {
- super.setCreatedBy(createdBy);
- }
-
- @JsonIgnore
- @Override
- public Long getUpdatedBy() {
- return super.getUpdatedBy();
- }
-
- @JsonIgnore
- @Override
- public void setUpdatedBy(Long updatedBy) {
- super.setUpdatedBy(updatedBy);
- }
-
- @JsonIgnore
- @Override
- public Instant getCreatedAt() {
- return super.getCreatedAt();
- }
-
- @JsonIgnore
- @Override
- public void setCreatedAt(Instant createdAt) {
- super.setCreatedAt(createdAt);
- }
-
- @JsonIgnore
- @Override
- public Instant getUpdatedAt() {
- return super.getUpdatedAt();
- }
-
- @JsonIgnore
- @Override
- public void setUpdatedAt(Instant updatedAt) {
- super.setUpdatedAt(updatedAt);
- }
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "name")
+ private String name;
+
+ @Column(name = "catch_phrase")
+ private String catchPhrase;
+
+ @Column(name = "bs")
+ private String bs;
+
+ @OneToOne(mappedBy = "company")
+ private User user;
+
+
+ public Company(String name, String catchPhrase, String bs) {
+ this.name = name;
+ this.catchPhrase = catchPhrase;
+ this.bs = bs;
+ }
+
+ @JsonIgnore
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ @JsonIgnore
+ @Override
+ public Long getCreatedBy() {
+ return super.getCreatedBy();
+ }
+
+ @JsonIgnore
+ @Override
+ public void setCreatedBy(Long createdBy) {
+ super.setCreatedBy(createdBy);
+ }
+
+ @JsonIgnore
+ @Override
+ public Long getUpdatedBy() {
+ return super.getUpdatedBy();
+ }
+
+ @JsonIgnore
+ @Override
+ public void setUpdatedBy(Long updatedBy) {
+ super.setUpdatedBy(updatedBy);
+ }
+
+ @JsonIgnore
+ @Override
+ public Instant getCreatedAt() {
+ return super.getCreatedAt();
+ }
+
+ @JsonIgnore
+ @Override
+ public void setCreatedAt(Instant createdAt) {
+ super.setCreatedAt(createdAt);
+ }
+
+ @JsonIgnore
+ @Override
+ public Instant getUpdatedAt() {
+ return super.getUpdatedAt();
+ }
+
+ @JsonIgnore
+ @Override
+ public void setUpdatedAt(Instant updatedAt) {
+ super.setUpdatedAt(updatedAt);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/model/user/Geo.java b/src/main/java/com/sopromadze/blogapi/model/user/Geo.java
index 8e18447c..f2845e22 100644
--- a/src/main/java/com/sopromadze/blogapi/model/user/Geo.java
+++ b/src/main/java/com/sopromadze/blogapi/model/user/Geo.java
@@ -2,105 +2,91 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.sopromadze.blogapi.model.audit.UserDateAudit;
-
-import javax.persistence.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
import java.time.Instant;
+@EqualsAndHashCode(callSuper = true)
@Entity
+@Data
+@NoArgsConstructor
@Table(name = "geo")
public class Geo extends UserDateAudit {
- @JsonIgnore
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @Column(name = "lat")
- private String lat;
-
- @Column(name = "lng")
- private String lng;
-
- @OneToOne(mappedBy = "geo")
- private Address address;
-
- public Geo(){
-
- }
-
- public Geo(String lat, String lng) {
- this.lat = lat;
- this.lng = lng;
- }
-
- public String getLat() {
- return lat;
- }
-
- public void setLat(String lat) {
- this.lat = lat;
- }
-
- public String getLng() {
- return lng;
- }
-
- public void setLng(String lng) {
- this.lng = lng;
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- @JsonIgnore
- @Override
- public Long getCreatedBy() {
- return super.getCreatedBy();
- }
-
- @JsonIgnore
- @Override
- public void setCreatedBy(Long createdBy) {
- super.setCreatedBy(createdBy);
- }
-
- @JsonIgnore
- @Override
- public Long getUpdatedBy() {
- return super.getUpdatedBy();
- }
-
- @JsonIgnore
- @Override
- public void setUpdatedBy(Long updatedBy) {
- super.setUpdatedBy(updatedBy);
- }
-
- @JsonIgnore
- @Override
- public Instant getCreatedAt() {
- return super.getCreatedAt();
- }
-
- @JsonIgnore
- @Override
- public void setCreatedAt(Instant createdAt) {
- super.setCreatedAt(createdAt);
- }
-
- @JsonIgnore
- @Override
- public Instant getUpdatedAt() {
- return super.getUpdatedAt();
- }
-
- @JsonIgnore
- @Override
- public void setUpdatedAt(Instant updatedAt) {
- super.setUpdatedAt(updatedAt);
- }
+ private static final long serialVersionUID = 1L;
+
+ @JsonIgnore
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "lat")
+ private String lat;
+
+ @Column(name = "lng")
+ private String lng;
+
+ @OneToOne(mappedBy = "geo")
+ private Address address;
+
+ public Geo(String lat, String lng) {
+ this.lat = lat;
+ this.lng = lng;
+ }
+
+ @JsonIgnore
+ @Override
+ public Long getCreatedBy() {
+ return super.getCreatedBy();
+ }
+
+ @JsonIgnore
+ @Override
+ public void setCreatedBy(Long createdBy) {
+ super.setCreatedBy(createdBy);
+ }
+
+ @JsonIgnore
+ @Override
+ public Long getUpdatedBy() {
+ return super.getUpdatedBy();
+ }
+
+ @JsonIgnore
+ @Override
+ public void setUpdatedBy(Long updatedBy) {
+ super.setUpdatedBy(updatedBy);
+ }
+
+ @JsonIgnore
+ @Override
+ public Instant getCreatedAt() {
+ return super.getCreatedAt();
+ }
+
+ @JsonIgnore
+ @Override
+ public void setCreatedAt(Instant createdAt) {
+ super.setCreatedAt(createdAt);
+ }
+
+ @JsonIgnore
+ @Override
+ public Instant getUpdatedAt() {
+ return super.getUpdatedAt();
+ }
+
+ @JsonIgnore
+ @Override
+ public void setUpdatedAt(Instant updatedAt) {
+ super.setUpdatedAt(updatedAt);
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/model/user/User.java b/src/main/java/com/sopromadze/blogapi/model/user/User.java
index bb8ecc4e..90f2e489 100644
--- a/src/main/java/com/sopromadze/blogapi/model/user/User.java
+++ b/src/main/java/com/sopromadze/blogapi/model/user/User.java
@@ -2,230 +2,191 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.sopromadze.blogapi.model.album.Album;
+import com.sopromadze.blogapi.model.Album;
import com.sopromadze.blogapi.model.audit.DateAudit;
-import com.sopromadze.blogapi.model.comment.Comment;
-import com.sopromadze.blogapi.model.post.Post;
+import com.sopromadze.blogapi.model.Comment;
+import com.sopromadze.blogapi.model.Post;
import com.sopromadze.blogapi.model.role.Role;
-import com.sopromadze.blogapi.model.todo.Todo;
+import com.sopromadze.blogapi.model.Todo;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
import org.hibernate.annotations.NaturalId;
-import javax.persistence.*;
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
-import java.time.Instant;
-import java.util.Date;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+@EqualsAndHashCode(callSuper = true)
@Entity
-@Table(name = "users", uniqueConstraints = {
- @UniqueConstraint(columnNames = {
- "username"
- }),
- @UniqueConstraint(columnNames = {
- "email"
- })
-})
+@Data
+@NoArgsConstructor
+@Table(name = "users", uniqueConstraints = { @UniqueConstraint(columnNames = { "username" }),
+ @UniqueConstraint(columnNames = { "email" }) })
public class User extends DateAudit {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name = "id")
- private Long id;
-
- @NotBlank
- @Column(name = "first_name")
- @Size(max = 40)
- private String firstName;
-
- @NotBlank
- @Column(name = "last_name")
- @Size(max = 40)
- private String lastName;
-
- @NotBlank
- @Column(name = "username")
- @Size(max = 15)
- private String username;
-
- @NotBlank
- @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
- @Size(max = 100)
- @Column(name = "password")
- private String password;
-
- @NotBlank
- @NaturalId
- @Size(max = 40)
- @Column(name = "email")
- @Email
- private String email;
-
- @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
- @JoinColumn(name = "address_id")
- private Address address;
-
- @Column(name = "phone")
- private String phone;
-
- @Column(name = "website")
- private String website;
-
- @ManyToMany(fetch = FetchType.EAGER)
- @JoinTable(name = "user_role",
- joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
- inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
- private List roles;
-
- @JsonIgnore
- @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
- private List todos;
-
- @JsonIgnore
- @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
- private List albums;
-
- @JsonIgnore
- @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
- private List posts;
-
- @JsonIgnore
- @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
- private List comments;
-
- @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
- @JoinColumn(name = "company_id")
- private Company company;
-
- public User(){
-
- }
-
- public User(String firstName, String lastName, String username, String email, String password) {
- this.firstName = firstName;
- this.lastName = lastName;
- this.username = username;
- this.email = email;
- this.password = password;
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getEmail() {
- return email;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public Address getAddress() {
- return address;
- }
-
- public void setAddress(Address address) {
- this.address = address;
- }
-
- public String getPhone() {
- return phone;
- }
-
- public void setPhone(String phone) {
- this.phone = phone;
- }
-
- public String getWebsite() {
- return website;
- }
-
- public void setWebsite(String website) {
- this.website = website;
- }
-
- public Company getCompany() {
- return company;
- }
+ private static final long serialVersionUID = 1L;
- public void setCompany(Company company) {
- this.company = company;
- }
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id")
+ private Long id;
- public List getTodos() {
- return todos;
- }
-
- public void setTodos(List todos) {
- this.todos = todos;
- }
-
- public List getAlbums() {
- return albums;
- }
+ @NotBlank
+ @Column(name = "first_name")
+ @Size(max = 40)
+ private String firstName;
- public void setAlbums(List albums) {
- this.albums = albums;
- }
+ @NotBlank
+ @Column(name = "last_name")
+ @Size(max = 40)
+ private String lastName;
- public String getFirstName() {
- return firstName;
- }
+ @NotBlank
+ @Column(name = "username")
+ @Size(max = 15)
+ private String username;
- public void setFirstName(String firstName) {
- this.firstName = firstName;
- }
+ @NotBlank
+ @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
+ @Size(max = 100)
+ @Column(name = "password")
+ private String password;
- public String getLastName() {
- return lastName;
- }
+ @NotBlank
+ @NaturalId
+ @Size(max = 40)
+ @Column(name = "email")
+ @Email
+ private String email;
- public void setLastName(String lastName) {
- this.lastName = lastName;
- }
+ @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
+ @JoinColumn(name = "address_id")
+ private Address address;
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public List getPosts() {
- return posts;
- }
+ @Column(name = "phone")
+ private String phone;
- public void setPosts(List posts) {
- this.posts = posts;
- }
+ @Column(name = "website")
+ private String website;
- public List getRoles() {
- return roles;
- }
+ @ManyToMany(fetch = FetchType.EAGER)
+ @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
+ private List roles;
- public void setRoles(List roles) {
- this.roles = roles;
- }
+ @JsonIgnore
+ @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
+ private List todos;
- public List getComments() {
- return comments;
- }
+ @JsonIgnore
+ @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
+ private List albums;
- public void setComments(List comments) {
- this.comments = comments;
- }
+ @JsonIgnore
+ @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
+ private List posts;
+
+ @JsonIgnore
+ @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
+ private List comments;
+
+ @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
+ @JoinColumn(name = "company_id")
+ private Company company;
+
+ public User(String firstName, String lastName, String username, String email, String password) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.username = username;
+ this.email = email;
+ this.password = password;
+ }
+
+
+
+ public List getTodos() {
+
+ return todos == null ? null : new ArrayList<>(todos);
+ }
+
+ public void setTodos(List todos) {
+
+ if (todos == null) {
+ this.todos = null;
+ } else {
+ this.todos = Collections.unmodifiableList(todos);
+ }
+ }
+
+ public List getAlbums() {
+
+ return albums == null ? null : new ArrayList<>(albums);
+ }
+
+ public void setAlbums(List albums) {
+
+ if (albums == null) {
+ this.albums = null;
+ } else {
+ this.albums = Collections.unmodifiableList(albums);
+ }
+ }
+
+
+ public List getPosts() {
+
+ return posts == null ? null : new ArrayList<>(posts);
+ }
+
+ public void setPosts(List posts) {
+
+ if (posts == null) {
+ this.posts = null;
+ } else {
+ this.posts = Collections.unmodifiableList(posts);
+ }
+ }
+
+ public List getRoles() {
+
+ return roles == null ? null : new ArrayList<>(roles);
+ }
+
+ public void setRoles(List roles) {
+
+ if (roles == null) {
+ this.roles = null;
+ } else {
+ this.roles = Collections.unmodifiableList(roles);
+ }
+ }
+
+ public List getComments() {
+ return comments == null ? null : new ArrayList<>(comments);
+ }
+
+ public void setComments(List comments) {
+
+ if (comments == null) {
+ this.comments = null;
+ } else {
+ this.comments = Collections.unmodifiableList(comments);
+ }
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/AlbumResponse.java b/src/main/java/com/sopromadze/blogapi/payload/AlbumResponse.java
new file mode 100644
index 00000000..ea57f7df
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/payload/AlbumResponse.java
@@ -0,0 +1,39 @@
+package com.sopromadze.blogapi.payload;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.sopromadze.blogapi.model.Photo;
+import com.sopromadze.blogapi.model.user.User;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@JsonInclude(Include.NON_NULL)
+public class AlbumResponse extends UserDateAuditPayload {
+ private Long id;
+
+ private String title;
+
+ private User user;
+
+ private List photo;
+
+ public List getPhoto() {
+
+ return photo == null ? null : new ArrayList<>(photo);
+ }
+
+ public void setPhoto(List photo) {
+
+ if (photo == null) {
+ this.photo = null;
+ } else {
+ this.photo = Collections.unmodifiableList(photo);
+ }
+ }
+}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/ApiResponse.java b/src/main/java/com/sopromadze/blogapi/payload/ApiResponse.java
index 94750969..4dfa14ab 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/ApiResponse.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/ApiResponse.java
@@ -1,27 +1,44 @@
package com.sopromadze.blogapi.payload;
-public class ApiResponse {
- private Boolean success;
- private String message;
-
- public ApiResponse(Boolean success, String message) {
- this.success = success;
- this.message = message;
- }
-
- public Boolean getSuccess() {
- return success;
- }
-
- public void setSuccess(Boolean success) {
- this.success = success;
- }
-
- public String getMessage() {
- return message;
- }
-
- public void setMessage(String message) {
- this.message = message;
- }
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import lombok.Data;
+import org.springframework.http.HttpStatus;
+
+import java.io.Serializable;
+
+@Data
+@JsonPropertyOrder({
+ "success",
+ "message"
+})
+public class ApiResponse implements Serializable {
+
+ @JsonIgnore
+ private static final long serialVersionUID = 7702134516418120340L;
+
+ @JsonProperty("success")
+ private Boolean success;
+
+ @JsonProperty("message")
+ private String message;
+
+ @JsonIgnore
+ private HttpStatus status;
+
+ public ApiResponse() {
+
+ }
+
+ public ApiResponse(Boolean success, String message) {
+ this.success = success;
+ this.message = message;
+ }
+
+ public ApiResponse(Boolean success, String message, HttpStatus httpStatus) {
+ this.success = success;
+ this.message = message;
+ this.status = httpStatus;
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/CommentRequest.java b/src/main/java/com/sopromadze/blogapi/payload/CommentRequest.java
index cf1b95c6..c88239ff 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/CommentRequest.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/CommentRequest.java
@@ -1,18 +1,13 @@
package com.sopromadze.blogapi.payload;
+import lombok.Data;
+
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
+@Data
public class CommentRequest {
- @NotBlank
- @Size(min = 10, message = "Comment body must be minimum 10 characters")
- private String body;
-
- public String getBody() {
- return body;
- }
-
- public void setBody(String body) {
- this.body = body;
- }
+ @NotBlank
+ @Size(min = 10, message = "Comment body must be minimum 10 characters")
+ private String body;
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/DateAuditPayload.java b/src/main/java/com/sopromadze/blogapi/payload/DateAuditPayload.java
new file mode 100644
index 00000000..7ec74a5a
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/payload/DateAuditPayload.java
@@ -0,0 +1,26 @@
+package com.sopromadze.blogapi.payload;
+
+import java.time.Instant;
+
+public abstract class DateAuditPayload {
+
+ private Instant createdAt;
+
+ private Instant updatedAt;
+
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Instant createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public Instant getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void setUpdatedAt(Instant updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/ExceptionResponse.java b/src/main/java/com/sopromadze/blogapi/payload/ExceptionResponse.java
index 0fa9eb08..a243981b 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/ExceptionResponse.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/ExceptionResponse.java
@@ -1,50 +1,38 @@
package com.sopromadze.blogapi.payload;
+import lombok.Data;
+
import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+@Data
public class ExceptionResponse {
- private String error;
- private Integer status;
- private List messages;
- private Instant timestamp;
-
- public ExceptionResponse(List messages, String error, Integer status) {
- this.messages = messages;
- this.error = error;
- this.status = status;
- this.timestamp = Instant.now();
- }
-
- public List getMessages() {
- return messages;
- }
-
- public void setMessages(List messages) {
- this.messages = messages;
- }
-
- public String getError() {
- return error;
- }
-
- public void setError(String error) {
- this.error = error;
- }
-
- public Integer getStatus() {
- return status;
- }
-
- public void setStatus(Integer status) {
- this.status = status;
- }
-
- public Instant getTimestamp() {
- return timestamp;
- }
-
- public void setTimestamp(Instant timestamp) {
- this.timestamp = timestamp;
- }
+ private String error;
+ private Integer status;
+ private List messages;
+ private Instant timestamp;
+
+ public ExceptionResponse(List messages, String error, Integer status) {
+ setMessages(messages);
+ this.error = error;
+ this.status = status;
+ this.timestamp = Instant.now();
+ }
+
+ public List getMessages() {
+
+ return messages == null ? null : new ArrayList<>(messages);
+ }
+
+ public final void setMessages(List messages) {
+
+ if (messages == null) {
+ this.messages = null;
+ } else {
+ this.messages = Collections.unmodifiableList(messages);
+ }
+ }
+
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/InfoRequest.java b/src/main/java/com/sopromadze/blogapi/payload/InfoRequest.java
index ad80ec3c..7baf210f 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/InfoRequest.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/InfoRequest.java
@@ -1,124 +1,35 @@
package com.sopromadze.blogapi.payload;
-import org.hibernate.validator.constraints.URL;
-import org.modelmapper.internal.bytebuddy.agent.builder.AgentBuilder;
+import lombok.Data;
-import javax.validation.GroupSequence;
import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.Size;
+@Data
public class InfoRequest {
- @NotBlank
- private String street;
- @NotBlank
- private String suite;
+ @NotBlank
+ private String street;
- @NotBlank
- private String city;
+ @NotBlank
+ private String suite;
- @NotBlank
- private String zipcode;
+ @NotBlank
+ private String city;
- private String companyName;
+ @NotBlank
+ private String zipcode;
- private String catchPhrase;
+ private String companyName;
- private String bs;
+ private String catchPhrase;
- private String website;
+ private String bs;
- private String phone;
+ private String website;
- private String lat;
+ private String phone;
- private String lng;
+ private String lat;
- public String getStreet() {
- return street;
- }
-
- public void setStreet(String street) {
- this.street = street;
- }
-
- public String getSuite() {
- return suite;
- }
-
- public void setSuite(String suite) {
- this.suite = suite;
- }
-
- public String getCity() {
- return city;
- }
-
- public void setCity(String city) {
- this.city = city;
- }
-
- public String getZipcode() {
- return zipcode;
- }
-
- public void setZipcode(String zipcode) {
- this.zipcode = zipcode;
- }
-
- public String getLat() {
- return lat;
- }
-
- public void setLat(String lat) {
- this.lat = lat;
- }
-
- public String getLng() {
- return lng;
- }
-
- public void setLng(String lng) {
- this.lng = lng;
- }
-
- public String getCompanyName() {
- return companyName;
- }
-
- public void setCompanyName(String companyName) {
- this.companyName = companyName;
- }
-
- public String getCatchPhrase() {
- return catchPhrase;
- }
-
- public void setCatchPhrase(String catchPhrase) {
- this.catchPhrase = catchPhrase;
- }
-
- public String getBs() {
- return bs;
- }
-
- public void setBs(String bs) {
- this.bs = bs;
- }
-
- public String getWebsite() {
- return website;
- }
-
- public void setWebsite(String website) {
- this.website = website;
- }
-
- public String getPhone() {
- return phone;
- }
-
- public void setPhone(String phone) {
- this.phone = phone;
- }
+ private String lng;
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/JwtAuthenticationResponse.java b/src/main/java/com/sopromadze/blogapi/payload/JwtAuthenticationResponse.java
index 47f2d1bb..148aa475 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/JwtAuthenticationResponse.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/JwtAuthenticationResponse.java
@@ -1,26 +1,14 @@
package com.sopromadze.blogapi.payload;
-public class JwtAuthenticationResponse {
- private String accessToken;
- private String tokenType = "Bearer";
-
- public JwtAuthenticationResponse(String accessToken) {
- this.accessToken = accessToken;
- }
+import lombok.Data;
- public String getAccessToken() {
- return accessToken;
- }
-
- public void setAccessToken(String accessToken) {
- this.accessToken = accessToken;
- }
+@Data
+public class JwtAuthenticationResponse {
+ private String accessToken;
+ private String tokenType = "Bearer";
- public String getTokenType() {
- return tokenType;
- }
+ public JwtAuthenticationResponse(String accessToken) {
+ this.accessToken = accessToken;
+ }
- public void setTokenType(String tokenType) {
- this.tokenType = tokenType;
- }
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/LoginRequest.java b/src/main/java/com/sopromadze/blogapi/payload/LoginRequest.java
index 4a04b5cb..ac1b0ee8 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/LoginRequest.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/LoginRequest.java
@@ -1,27 +1,14 @@
package com.sopromadze.blogapi.payload;
+import lombok.Data;
+
import javax.validation.constraints.NotBlank;
+@Data
public class LoginRequest {
- @NotBlank
- private String usernameOrEmail;
-
- @NotBlank
- private String password;
-
- public String getUsernameOrEmail() {
- return usernameOrEmail;
- }
-
- public void setUsernameOrEmail(String usernameOrEmail) {
- this.usernameOrEmail = usernameOrEmail;
- }
-
- public String getPassword() {
- return password;
- }
+ @NotBlank
+ private String usernameOrEmail;
- public void setPassword(String password) {
- this.password = password;
- }
+ @NotBlank
+ private String password;
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/PagedResponse.java b/src/main/java/com/sopromadze/blogapi/payload/PagedResponse.java
index 651ad5aa..9e73e40e 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/PagedResponse.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/PagedResponse.java
@@ -1,73 +1,48 @@
package com.sopromadze.blogapi.payload;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+@Data
public class PagedResponse {
- private List content;
- private int page;
- private int size;
- private long totalElements;
- private int totalPages;
- private boolean last;
-
- public PagedResponse(){
-
- }
-
- public PagedResponse(List content, int page, int size, long totalElements, int totalPages, boolean last) {
- this.content = content;
- this.page = page;
- this.size = size;
- this.totalElements = totalElements;
- this.totalPages = totalPages;
- this.last = last;
- }
-
- public List getContent() {
- return content;
- }
-
- public void setContent(List posts) {
- this.content = posts;
- }
-
- public int getPage() {
- return page;
- }
-
- public void setPage(int page) {
- this.page = page;
- }
-
- public int getSize() {
- return size;
- }
-
- public void setSize(int size) {
- this.size = size;
- }
-
- public long getTotalElements() {
- return totalElements;
- }
-
- public void setTotalElements(long totalElements) {
- this.totalElements = totalElements;
- }
-
- public int getTotalPages() {
- return totalPages;
- }
-
- public void setTotalPages(int totalPages) {
- this.totalPages = totalPages;
- }
-
- public boolean isLast() {
- return last;
- }
-
- public void setLast(boolean last) {
- this.last = last;
- }
+ private List content;
+ private int page;
+ private int size;
+ private long totalElements;
+ private int totalPages;
+ private boolean last;
+
+ public PagedResponse() {
+
+ }
+
+ public PagedResponse(List content, int page, int size, long totalElements, int totalPages, boolean last) {
+ setContent(content);
+ this.page = page;
+ this.size = size;
+ this.totalElements = totalElements;
+ this.totalPages = totalPages;
+ this.last = last;
+ }
+
+ public List getContent() {
+ return content == null ? null : new ArrayList<>(content);
+ }
+
+ public final void setContent(List content) {
+ if (content == null) {
+ this.content = null;
+ } else {
+ this.content = Collections.unmodifiableList(content);
+ }
+ }
+
+
+
+ public boolean isLast() {
+ return last;
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/PhotoRequest.java b/src/main/java/com/sopromadze/blogapi/payload/PhotoRequest.java
index 6a7ac57d..74950489 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/PhotoRequest.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/PhotoRequest.java
@@ -1,55 +1,26 @@
package com.sopromadze.blogapi.payload;
+import lombok.Data;
+
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
+@Data
public class PhotoRequest {
- @NotBlank
- @Size(min = 3)
- private String title;
-
- @NotBlank
- @Size(min = 10)
- private String url;
-
- @NotBlank
- @Size(min = 10)
- private String thumbnailUrl;
-
- @NotNull
- private Long albumId;
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getUrl() {
- return url;
- }
-
- public void setUrl(String url) {
- this.url = url;
- }
-
- public String getThumbnailUrl() {
- return thumbnailUrl;
- }
+ @NotBlank
+ @Size(min = 3)
+ private String title;
- public void setThumbnailUrl(String thumbnailUrl) {
- this.thumbnailUrl = thumbnailUrl;
- }
+ @NotBlank
+ @Size(min = 10)
+ private String url;
- public Long getAlbumId() {
- return albumId;
- }
+ @NotBlank
+ @Size(min = 10)
+ private String thumbnailUrl;
- public void setAlbumId(Long albumId) {
- this.albumId = albumId;
- }
+ @NotNull
+ private Long albumId;
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/PhotoResponse.java b/src/main/java/com/sopromadze/blogapi/payload/PhotoResponse.java
index 2038142f..e695b977 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/PhotoResponse.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/PhotoResponse.java
@@ -1,57 +1,21 @@
package com.sopromadze.blogapi.payload;
-public class PhotoResponse {
- private Long id;
- private String title;
- private String url;
- private String thumbnailUrl;
- private Long albumId;
-
- public PhotoResponse(Long id, String title, String url, String thumbnailUrl, Long albumId) {
- this.id = id;
- this.title = title;
- this.url = url;
- this.thumbnailUrl = thumbnailUrl;
- this.albumId = albumId;
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
+import lombok.Data;
- public String getUrl() {
- return url;
- }
-
- public void setUrl(String url) {
- this.url = url;
- }
-
- public String getThumbnailUrl() {
- return thumbnailUrl;
- }
-
- public void setThumbnailUrl(String thumbnailUrl) {
- this.thumbnailUrl = thumbnailUrl;
- }
-
- public Long getAlbumId() {
- return albumId;
- }
+@Data
+public class PhotoResponse {
+ private Long id;
+ private String title;
+ private String url;
+ private String thumbnailUrl;
+ private Long albumId;
+
+ public PhotoResponse(Long id, String title, String url, String thumbnailUrl, Long albumId) {
+ this.id = id;
+ this.title = title;
+ this.url = url;
+ this.thumbnailUrl = thumbnailUrl;
+ this.albumId = albumId;
+ }
- public void setAlbumId(Long albumId) {
- this.albumId = albumId;
- }
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/PostRequest.java b/src/main/java/com/sopromadze/blogapi/payload/PostRequest.java
new file mode 100644
index 00000000..28d7ed13
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/payload/PostRequest.java
@@ -0,0 +1,41 @@
+package com.sopromadze.blogapi.payload;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@Data
+public class PostRequest {
+
+ @NotBlank
+ @Size(min = 10)
+ private String title;
+
+ @NotBlank
+ @Size(min = 50)
+ private String body;
+
+ @NotNull
+ private Long categoryId;
+
+ private List tags;
+
+ public List getTags() {
+
+ return tags == null ? Collections.emptyList() : new ArrayList<>(tags);
+ }
+
+ public void setTags(List tags) {
+
+ if (tags == null) {
+ this.tags = null;
+ } else {
+ this.tags = Collections.unmodifiableList(tags);
+ }
+ }
+}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/PostResponse.java b/src/main/java/com/sopromadze/blogapi/payload/PostResponse.java
index 7298316b..f054e518 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/PostResponse.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/PostResponse.java
@@ -1,51 +1,31 @@
package com.sopromadze.blogapi.payload;
-import java.time.Instant;
+import lombok.Data;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@Data
public class PostResponse {
- private Long id;
- private String title;
- private String body;
- private UserSummary createdBy;
- private Instant updatedDateTime;
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getBody() {
- return body;
- }
-
- public void setBody(String body) {
- this.body = body;
- }
-
- public UserSummary getCreatedBy() {
- return createdBy;
- }
-
- public void setCreatedBy(UserSummary createdBy) {
- this.createdBy = createdBy;
- }
-
- public Instant getUpdatedDateTime() {
- return updatedDateTime;
- }
-
- public void setUpdatedDateTime(Instant updatedDateTime) {
- this.updatedDateTime = updatedDateTime;
- }
+ private String title;
+ private String body;
+ private String category;
+ private List tags;
+
+
+
+ public List getTags() {
+
+ return tags == null ? null : new ArrayList<>(tags);
+ }
+
+ public void setTags(List tags) {
+
+ if (tags == null) {
+ this.tags = null;
+ } else {
+ this.tags = Collections.unmodifiableList(tags);
+ }
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/SignUpRequest.java b/src/main/java/com/sopromadze/blogapi/payload/SignUpRequest.java
index 46eeb955..3f993f13 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/SignUpRequest.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/SignUpRequest.java
@@ -1,68 +1,31 @@
package com.sopromadze.blogapi.payload;
+import lombok.Data;
+
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
+@Data
public class SignUpRequest {
- @NotBlank
- @Size(min = 4, max = 40)
- private String firstName;
-
- @NotBlank
- @Size(min = 4, max = 40)
- private String lastName;
-
- @NotBlank
- @Size(min = 3, max = 15)
- private String username;
-
- @NotBlank
- @Size(max = 40)
- @Email
- private String email;
-
- @NotBlank
- @Size(min = 6, max = 20)
- private String password;
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getEmail() {
- return email;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public String getFirstName() {
- return firstName;
- }
-
- public void setFirstName(String firstName) {
- this.firstName = firstName;
- }
-
- public String getLastName() {
- return lastName;
- }
-
- public void setLastName(String lastName) {
- this.lastName = lastName;
- }
+ @NotBlank
+ @Size(min = 4, max = 40)
+ private String firstName;
+
+ @NotBlank
+ @Size(min = 4, max = 40)
+ private String lastName;
+
+ @NotBlank
+ @Size(min = 3, max = 15)
+ private String username;
+
+ @NotBlank
+ @Size(max = 40)
+ @Email
+ private String email;
+
+ @NotBlank
+ @Size(min = 6, max = 20)
+ private String password;
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/UserDateAuditPayload.java b/src/main/java/com/sopromadze/blogapi/payload/UserDateAuditPayload.java
new file mode 100644
index 00000000..834f095d
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/payload/UserDateAuditPayload.java
@@ -0,0 +1,13 @@
+package com.sopromadze.blogapi.payload;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public abstract class UserDateAuditPayload extends DateAuditPayload {
+ private Long createdBy;
+
+ private Long updatedBy;
+
+}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/UserIdentityAvailability.java b/src/main/java/com/sopromadze/blogapi/payload/UserIdentityAvailability.java
index d2dffb93..c5d09fc2 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/UserIdentityAvailability.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/UserIdentityAvailability.java
@@ -1,17 +1,11 @@
package com.sopromadze.blogapi.payload;
-public class UserIdentityAvailability {
- private Boolean available;
-
- public UserIdentityAvailability(Boolean available) {
- this.available = available;
- }
+import lombok.AllArgsConstructor;
+import lombok.Data;
- public Boolean getAvailable() {
- return available;
- }
+@Data
+@AllArgsConstructor
+public class UserIdentityAvailability {
+ private Boolean available;
- public void setAvailable(Boolean available) {
- this.available = available;
- }
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/UserProfile.java b/src/main/java/com/sopromadze/blogapi/payload/UserProfile.java
index 968ede71..b428ea12 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/UserProfile.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/UserProfile.java
@@ -2,125 +2,25 @@
import com.sopromadze.blogapi.model.user.Address;
import com.sopromadze.blogapi.model.user.Company;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
import java.time.Instant;
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
public class UserProfile {
- private Long id;
- private String username;
- private String firstName;
- private String lastName;
- private Instant joinedAt;
- private String email;
- private Address address;
- private String phone;
- private String website;
- private Company company;
- private Long postCount;
-
- public UserProfile(){
-
- }
-
- public UserProfile(Long id, String username, String firstName, String lastName, Instant joinedAt, String email, Address address, String phone, String website, Company company, Long postCount) {
- this.id = id;
- this.username = username;
- this.firstName = firstName;
- this.lastName = lastName;
- this.joinedAt = joinedAt;
- this.email = email;
- this.address = address;
- this.phone = phone;
- this.website = website;
- this.company = company;
- this.postCount = postCount;
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getFirstName() {
- return firstName;
- }
-
- public void setFirstName(String firstName) {
- this.firstName = firstName;
- }
-
- public String getLastName() {
- return lastName;
- }
-
- public void setLastName(String lastName) {
- this.lastName = lastName;
- }
-
- public Instant getJoinedAt() {
- return joinedAt;
- }
-
- public void setJoinedAt(Instant joinedAt) {
- this.joinedAt = joinedAt;
- }
-
- public String getEmail() {
- return email;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public Address getAddress() {
- return address;
- }
-
- public void setAddress(Address address) {
- this.address = address;
- }
-
- public String getPhone() {
- return phone;
- }
-
- public void setPhone(String phone) {
- this.phone = phone;
- }
-
- public String getWebsite() {
- return website;
- }
-
- public void setWebsite(String website) {
- this.website = website;
- }
-
- public Company getCompany() {
- return company;
- }
-
- public void setCompany(Company company) {
- this.company = company;
- }
-
- public Long getPostCount() {
- return postCount;
- }
-
- public void setPostCount(Long postCount) {
- this.postCount = postCount;
- }
+ private Long id;
+ private String username;
+ private String firstName;
+ private String lastName;
+ private Instant joinedAt;
+ private String email;
+ private Address address;
+ private String phone;
+ private String website;
+ private Company company;
+ private Long postCount;
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/UserSummary.java b/src/main/java/com/sopromadze/blogapi/payload/UserSummary.java
index f1a1c3c3..7fe8fe92 100644
--- a/src/main/java/com/sopromadze/blogapi/payload/UserSummary.java
+++ b/src/main/java/com/sopromadze/blogapi/payload/UserSummary.java
@@ -1,47 +1,13 @@
package com.sopromadze.blogapi.payload;
-public class UserSummary {
- private Long id;
- private String username;
- private String firstName;
- private String lastName;
-
- public UserSummary(Long id, String username, String firstName, String lastName) {
- this.id = id;
- this.username = username;
- this.firstName = firstName;
- this.lastName = lastName;
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getUsername() {
- return username;
- }
+import lombok.AllArgsConstructor;
+import lombok.Data;
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getFirstName() {
- return firstName;
- }
-
- public void setFirstName(String firstName) {
- this.firstName = firstName;
- }
-
- public String getLastName() {
- return lastName;
- }
-
- public void setLastName(String lastName) {
- this.lastName = lastName;
- }
+@Data
+@AllArgsConstructor
+public class UserSummary {
+ private Long id;
+ private String username;
+ private String firstName;
+ private String lastName;
}
diff --git a/src/main/java/com/sopromadze/blogapi/payload/request/AlbumRequest.java b/src/main/java/com/sopromadze/blogapi/payload/request/AlbumRequest.java
new file mode 100644
index 00000000..83e4f1eb
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/payload/request/AlbumRequest.java
@@ -0,0 +1,36 @@
+package com.sopromadze.blogapi.payload.request;
+
+import com.sopromadze.blogapi.model.Photo;
+import com.sopromadze.blogapi.model.user.User;
+import com.sopromadze.blogapi.payload.UserDateAuditPayload;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@Data
+public class AlbumRequest extends UserDateAuditPayload {
+
+ private Long id;
+
+ private String title;
+
+ private User user;
+
+ private List photo;
+
+ public List getPhoto() {
+
+ return photo == null ? null : new ArrayList<>(photo);
+ }
+
+ public void setPhoto(List photo) {
+
+ if (photo == null) {
+ this.photo = null;
+ } else {
+ this.photo = Collections.unmodifiableList(photo);
+ }
+ }
+}
diff --git a/src/main/java/com/sopromadze/blogapi/repository/AlbumRepository.java b/src/main/java/com/sopromadze/blogapi/repository/AlbumRepository.java
index af696383..12d6837f 100644
--- a/src/main/java/com/sopromadze/blogapi/repository/AlbumRepository.java
+++ b/src/main/java/com/sopromadze/blogapi/repository/AlbumRepository.java
@@ -1,13 +1,12 @@
package com.sopromadze.blogapi.repository;
-import com.sopromadze.blogapi.model.album.Album;
+import com.sopromadze.blogapi.model.Album;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
-import org.springframework.data.repository.CrudRepository;
-import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
-public interface AlbumRepository extends PagingAndSortingRepository {
- Page findByCreatedBy(Long userId, Pageable pageable);
+public interface AlbumRepository extends JpaRepository {
+ Page findByCreatedBy(Long userId, Pageable pageable);
}
diff --git a/src/main/java/com/sopromadze/blogapi/repository/CategoryRepository.java b/src/main/java/com/sopromadze/blogapi/repository/CategoryRepository.java
new file mode 100644
index 00000000..55c00914
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/repository/CategoryRepository.java
@@ -0,0 +1,10 @@
+package com.sopromadze.blogapi.repository;
+
+import com.sopromadze.blogapi.model.Category;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface CategoryRepository extends JpaRepository {
+
+}
diff --git a/src/main/java/com/sopromadze/blogapi/repository/CommentRepository.java b/src/main/java/com/sopromadze/blogapi/repository/CommentRepository.java
index 0465ee4f..b4fb3be3 100644
--- a/src/main/java/com/sopromadze/blogapi/repository/CommentRepository.java
+++ b/src/main/java/com/sopromadze/blogapi/repository/CommentRepository.java
@@ -1,13 +1,12 @@
package com.sopromadze.blogapi.repository;
-import com.sopromadze.blogapi.model.comment.Comment;
+import com.sopromadze.blogapi.model.Comment;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
-import org.springframework.data.repository.CrudRepository;
-import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
-public interface CommentRepository extends PagingAndSortingRepository {
- Page findByPostId(Long postId, Pageable pageable);
+public interface CommentRepository extends JpaRepository {
+ Page findByPostId(Long postId, Pageable pageable);
}
diff --git a/src/main/java/com/sopromadze/blogapi/repository/PhotoRepository.java b/src/main/java/com/sopromadze/blogapi/repository/PhotoRepository.java
index 4f461e32..a6f52698 100644
--- a/src/main/java/com/sopromadze/blogapi/repository/PhotoRepository.java
+++ b/src/main/java/com/sopromadze/blogapi/repository/PhotoRepository.java
@@ -1,13 +1,12 @@
package com.sopromadze.blogapi.repository;
-import com.sopromadze.blogapi.model.photo.Photo;
+import com.sopromadze.blogapi.model.Photo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
-import org.springframework.data.repository.CrudRepository;
-import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
-public interface PhotoRepository extends PagingAndSortingRepository {
- Page findByAlbumId(Long albumId, Pageable pageable);
+public interface PhotoRepository extends JpaRepository {
+ Page findByAlbumId(Long albumId, Pageable pageable);
}
diff --git a/src/main/java/com/sopromadze/blogapi/repository/PostRepository.java b/src/main/java/com/sopromadze/blogapi/repository/PostRepository.java
index c3443d3b..a748afb8 100644
--- a/src/main/java/com/sopromadze/blogapi/repository/PostRepository.java
+++ b/src/main/java/com/sopromadze/blogapi/repository/PostRepository.java
@@ -1,14 +1,21 @@
package com.sopromadze.blogapi.repository;
-import com.sopromadze.blogapi.model.post.Post;
+import com.sopromadze.blogapi.model.Post;
+import com.sopromadze.blogapi.model.Tag;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
-import org.springframework.data.repository.CrudRepository;
-import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
+import java.util.List;
+
@Repository
-public interface PostRepository extends PagingAndSortingRepository {
- Page findByCreatedBy(Long userId, Pageable pageable);
- Long countByCreatedBy(Long userId);
+public interface PostRepository extends JpaRepository {
+ Page findByCreatedBy(Long userId, Pageable pageable);
+
+ Page findByCategory(Long categoryId, Pageable pageable);
+
+ Page findByTags(List tags, Pageable pageable);
+
+ Long countByCreatedBy(Long userId);
}
diff --git a/src/main/java/com/sopromadze/blogapi/repository/RoleRepository.java b/src/main/java/com/sopromadze/blogapi/repository/RoleRepository.java
index 6c666d0f..067294c6 100644
--- a/src/main/java/com/sopromadze/blogapi/repository/RoleRepository.java
+++ b/src/main/java/com/sopromadze/blogapi/repository/RoleRepository.java
@@ -2,10 +2,10 @@
import com.sopromadze.blogapi.model.role.Role;
import com.sopromadze.blogapi.model.role.RoleName;
-import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
-public interface RoleRepository extends CrudRepository {
- Optional findByName(RoleName name);
+public interface RoleRepository extends JpaRepository {
+ Optional findByName(RoleName name);
}
diff --git a/src/main/java/com/sopromadze/blogapi/repository/TagRepository.java b/src/main/java/com/sopromadze/blogapi/repository/TagRepository.java
new file mode 100644
index 00000000..e9620636
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/repository/TagRepository.java
@@ -0,0 +1,10 @@
+package com.sopromadze.blogapi.repository;
+
+import com.sopromadze.blogapi.model.Tag;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface TagRepository extends JpaRepository {
+ Tag findByName(String name);
+}
diff --git a/src/main/java/com/sopromadze/blogapi/repository/TodoRepository.java b/src/main/java/com/sopromadze/blogapi/repository/TodoRepository.java
index f619980e..3cb55e71 100644
--- a/src/main/java/com/sopromadze/blogapi/repository/TodoRepository.java
+++ b/src/main/java/com/sopromadze/blogapi/repository/TodoRepository.java
@@ -1,12 +1,12 @@
package com.sopromadze.blogapi.repository;
-import com.sopromadze.blogapi.model.todo.Todo;
+import com.sopromadze.blogapi.model.Todo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
-import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
-public interface TodoRepository extends CrudRepository {
- Page findByCreatedBy(Long userId, Pageable pageable);
+public interface TodoRepository extends JpaRepository {
+ Page findByCreatedBy(Long userId, Pageable pageable);
}
diff --git a/src/main/java/com/sopromadze/blogapi/repository/UserRepository.java b/src/main/java/com/sopromadze/blogapi/repository/UserRepository.java
index fd46b823..94290248 100644
--- a/src/main/java/com/sopromadze/blogapi/repository/UserRepository.java
+++ b/src/main/java/com/sopromadze/blogapi/repository/UserRepository.java
@@ -1,20 +1,32 @@
package com.sopromadze.blogapi.repository;
+import com.sopromadze.blogapi.exception.ResourceNotFoundException;
import com.sopromadze.blogapi.model.user.User;
-import org.springframework.data.repository.CrudRepository;
-import org.springframework.data.repository.PagingAndSortingRepository;
+import com.sopromadze.blogapi.security.UserPrincipal;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
-import javax.swing.text.html.Option;
import javax.validation.constraints.NotBlank;
-import java.util.List;
import java.util.Optional;
@Repository
-public interface UserRepository extends PagingAndSortingRepository {
- Optional findByUsername(@NotBlank String username);
- Optional findByEmail(@NotBlank String email);
- Boolean existsByUsername(@NotBlank String username);
- Boolean existsByEmail(@NotBlank String email);
- Optional findByUsernameOrEmail(String username, String email);
+public interface UserRepository extends JpaRepository {
+ Optional findByUsername(@NotBlank String username);
+
+ Optional findByEmail(@NotBlank String email);
+
+ Boolean existsByUsername(@NotBlank String username);
+
+ Boolean existsByEmail(@NotBlank String email);
+
+ Optional findByUsernameOrEmail(String username, String email);
+
+ default User getUser(UserPrincipal currentUser) {
+ return getUserByName(currentUser.getUsername());
+ }
+
+ default User getUserByName(String username) {
+ return findByUsername(username)
+ .orElseThrow(() -> new ResourceNotFoundException("User", "username", username));
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/security/CurrentUser.java b/src/main/java/com/sopromadze/blogapi/security/CurrentUser.java
index 2833d6a4..400b4a48 100644
--- a/src/main/java/com/sopromadze/blogapi/security/CurrentUser.java
+++ b/src/main/java/com/sopromadze/blogapi/security/CurrentUser.java
@@ -2,9 +2,13 @@
import org.springframework.security.core.annotation.AuthenticationPrincipal;
-import java.lang.annotation.*;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
-@Target({ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
+@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
diff --git a/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationEntryPoint.java b/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationEntryPoint.java
index bc0913eb..b9f644ab 100644
--- a/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationEntryPoint.java
+++ b/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationEntryPoint.java
@@ -13,10 +13,12 @@
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
- private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
- @Override
- public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
- LOGGER.error("Responding with unauthorized error. Message - {}", e.getMessage());
- httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Sorry, You're not authorized to access this resource.");
- }
+ private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
+
+ @Override
+ public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e)
+ throws IOException, ServletException {
+ LOGGER.error("Responding with unauthorized error. Message - {}", e.getMessage());
+ httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Sorry, You're not authorized to access this resource.");
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationFilter.java b/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationFilter.java
index 8e09deb7..3c162554 100644
--- a/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationFilter.java
+++ b/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationFilter.java
@@ -18,40 +18,40 @@
import java.io.IOException;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
- @Autowired
- private JwtTokenProvider tokenProvider;
-
- @Autowired
- private CustomUserDetailsService customUserDetailsService;
-
- private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
-
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
- try{
- String jwt = getJwtFromRequest(request);
-
- if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)){
- Long userId = tokenProvider.getUserIdFromJWT(jwt);
-
- UserDetails userDetails = customUserDetailsService.loadUserById(userId);
- UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
- authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
-
- SecurityContextHolder.getContext().setAuthentication(authenticationToken);
- }
- } catch (Exception ex){
- LOGGER.error("Could not set user authentication in security context", ex);
- }
-
- filterChain.doFilter(request, response);
- }
-
- private String getJwtFromRequest(HttpServletRequest request){
- String bearerToken = request.getHeader("Authorization");
- if(StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")){
- return bearerToken.substring(7, bearerToken.length());
- }
- return null;
- }
+ private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
+ @Autowired
+ private JwtTokenProvider tokenProvider;
+ @Autowired
+ private CustomUserDetailsService customUserDetailsService;
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+ try {
+ String jwt = getJwtFromRequest(request);
+
+ if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
+ Long userId = tokenProvider.getUserIdFromJWT(jwt);
+
+ UserDetails userDetails = customUserDetailsService.loadUserById(userId);
+ UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null,
+ userDetails.getAuthorities());
+ authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+
+ SecurityContextHolder.getContext().setAuthentication(authenticationToken);
+ }
+ } catch (Exception ex) {
+ LOGGER.error("Could not set user authentication in security context", ex);
+ }
+
+ filterChain.doFilter(request, response);
+ }
+
+ private String getJwtFromRequest(HttpServletRequest request) {
+ String bearerToken = request.getHeader("Authorization");
+ if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
+ return bearerToken.substring(7, bearerToken.length());
+ }
+ return null;
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/security/JwtTokenProvider.java b/src/main/java/com/sopromadze/blogapi/security/JwtTokenProvider.java
index 3472d79a..77fad327 100644
--- a/src/main/java/com/sopromadze/blogapi/security/JwtTokenProvider.java
+++ b/src/main/java/com/sopromadze/blogapi/security/JwtTokenProvider.java
@@ -1,6 +1,12 @@
package com.sopromadze.blogapi.security;
-import io.jsonwebtoken.*;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.MalformedJwtException;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.SignatureException;
+import io.jsonwebtoken.UnsupportedJwtException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
@@ -11,52 +17,52 @@
@Component
public class JwtTokenProvider {
- private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenProvider.class);
-
- @Value(value = "${app.jwtSecret}")
- private String jwtSecret;
-
- @Value(value = "${app.jwtExpirationInMs}")
- private int jwtExpirationInMs;
-
- public String generateToken(Authentication authentication){
- UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
-
- Date now = new Date();
- Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);
-
- return Jwts.builder()
- .setSubject(Long.toString(userPrincipal.getId()))
- .setIssuedAt(new Date())
- .setExpiration(expiryDate)
- .signWith(SignatureAlgorithm.HS512, jwtSecret)
- .compact();
- }
-
- public Long getUserIdFromJWT(String token){
- Claims claims = Jwts.parser()
- .setSigningKey(jwtSecret)
- .parseClaimsJws(token)
- .getBody();
-
- return Long.parseLong(claims.getSubject());
- }
-
- public boolean validateToken(String authToken){
- try{
- Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
- return true;
- } catch (SignatureException ex){
- LOGGER.error("Invalid JWT signature");
- } catch (MalformedJwtException ex){
- LOGGER.error("Invalid JWT token");
- } catch (ExpiredJwtException ex){
- LOGGER.error("Expired JWT token");
- } catch (UnsupportedJwtException ex){
- LOGGER.error("Unsupported JWT token");
- } catch (IllegalArgumentException ex){
- LOGGER.error("JWT claims string is empty");
- }
- return false;
- }
+ private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenProvider.class);
+
+ @Value(value = "${app.jwtSecret}")
+ private String jwtSecret;
+
+ @Value(value = "${app.jwtExpirationInMs}")
+ private int jwtExpirationInMs;
+
+ public String generateToken(Authentication authentication) {
+ UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
+
+ Date now = new Date();
+ Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);
+
+ return Jwts.builder()
+ .setSubject(Long.toString(userPrincipal.getId()))
+ .setIssuedAt(new Date())
+ .setExpiration(expiryDate)
+ .signWith(SignatureAlgorithm.HS512, jwtSecret)
+ .compact();
+ }
+
+ public Long getUserIdFromJWT(String token) {
+ Claims claims = Jwts.parser()
+ .setSigningKey(jwtSecret)
+ .parseClaimsJws(token)
+ .getBody();
+
+ return Long.valueOf(claims.getSubject());
+ }
+
+ public boolean validateToken(String authToken) {
+ try {
+ Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
+ return true;
+ } catch (SignatureException ex) {
+ LOGGER.error("Invalid JWT signature");
+ } catch (MalformedJwtException ex) {
+ LOGGER.error("Invalid JWT token");
+ } catch (ExpiredJwtException ex) {
+ LOGGER.error("Expired JWT token");
+ } catch (UnsupportedJwtException ex) {
+ LOGGER.error("Unsupported JWT token");
+ } catch (IllegalArgumentException ex) {
+ LOGGER.error("JWT claims string is empty");
+ }
+ return false;
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/security/UserPrincipal.java b/src/main/java/com/sopromadze/blogapi/security/UserPrincipal.java
index 40cd9028..3402459f 100644
--- a/src/main/java/com/sopromadze/blogapi/security/UserPrincipal.java
+++ b/src/main/java/com/sopromadze/blogapi/security/UserPrincipal.java
@@ -6,112 +6,116 @@
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class UserPrincipal implements UserDetails {
- private Long id;
-
- private String firstName;
-
- private String lastName;
-
- private String username;
-
- @JsonIgnore
- private String email;
-
- @JsonIgnore
- private String password;
-
- private Collection extends GrantedAuthority> authorities;
-
- public UserPrincipal(Long id, String firstName, String lastName, String username, String email, String password, Collection extends GrantedAuthority> authorities) {
- this.id = id;
- this.firstName = firstName;
- this.lastName = lastName;
- this.username = username;
- this.email = email;
- this.password = password;
- this.authorities = authorities;
- }
-
- public static UserPrincipal create(User user){
- List authorities = user.getRoles().stream().map(role ->
- new SimpleGrantedAuthority(role.getName().name())).collect(Collectors.toList());
-
- return new UserPrincipal(
- user.getId(),
- user.getFirstName(),
- user.getLastName(),
- user.getUsername(),
- user.getEmail(),
- user.getPassword(),
- authorities
- );
- }
-
- public Long getId(){
- return id;
- }
-
- public String getEmail(){
- return email;
- }
-
- @Override
- public Collection extends GrantedAuthority> getAuthorities() {
- return authorities;
- }
-
- @Override
- public String getPassword() {
- return password;
- }
-
- @Override
- public String getUsername() {
- return username;
- }
-
- @Override
- public boolean isAccountNonExpired() {
- return true;
- }
-
- @Override
- public boolean isAccountNonLocked() {
- return true;
- }
-
- @Override
- public boolean isCredentialsNonExpired() {
- return true;
- }
-
- @Override
- public boolean isEnabled() {
- return true;
- }
-
- public boolean equals(Object object){
- if(this == object) return true;
- if(object == null || getClass() != object.getClass()) return false;
- UserPrincipal that = (UserPrincipal) object;
- return Objects.equals(id, that.id);
- }
-
- public int hashCode(){
- return Objects.hash(id);
- }
-
- public String getFirstName() {
- return firstName;
- }
-
- public String getLastName() {
- return lastName;
- }
+ private static final long serialVersionUID = 1L;
+
+ private Long id;
+
+ private String firstName;
+
+ private String lastName;
+
+ private String username;
+
+ @JsonIgnore
+ private String email;
+
+ @JsonIgnore
+ private String password;
+
+ private Collection extends GrantedAuthority> authorities;
+
+ public UserPrincipal(Long id, String firstName, String lastName, String username, String email, String password,
+ Collection extends GrantedAuthority> authorities) {
+ this.id = id;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.username = username;
+ this.email = email;
+ this.password = password;
+
+ if (authorities == null) {
+ this.authorities = null;
+ } else {
+ this.authorities = new ArrayList<>(authorities);
+ }
+ }
+
+ public static UserPrincipal create(User user) {
+ List authorities = user.getRoles().stream()
+ .map(role -> new SimpleGrantedAuthority(role.getName().name())).collect(Collectors.toList());
+
+ return new UserPrincipal(user.getId(), user.getFirstName(), user.getLastName(), user.getUsername(),
+ user.getEmail(), user.getPassword(), authorities);
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return authorities == null ? null : new ArrayList<>(authorities);
+ }
+
+ @Override
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public String getUsername() {
+ return username;
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ public boolean equals(Object object) {
+ if (this == object)
+ return true;
+ if (object == null || getClass() != object.getClass())
+ return false;
+ UserPrincipal that = (UserPrincipal) object;
+ return Objects.equals(id, that.id);
+ }
+
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
}
diff --git a/src/main/java/com/sopromadze/blogapi/service/AlbumService.java b/src/main/java/com/sopromadze/blogapi/service/AlbumService.java
index 91e26d88..ef4a3621 100644
--- a/src/main/java/com/sopromadze/blogapi/service/AlbumService.java
+++ b/src/main/java/com/sopromadze/blogapi/service/AlbumService.java
@@ -1,108 +1,25 @@
package com.sopromadze.blogapi.service;
-import com.sopromadze.blogapi.exception.BadRequestException;
-import com.sopromadze.blogapi.exception.ResourceNotFoundException;
-import com.sopromadze.blogapi.model.album.Album;
-import com.sopromadze.blogapi.model.role.RoleName;
-import com.sopromadze.blogapi.model.user.User;
+import com.sopromadze.blogapi.model.Album;
+import com.sopromadze.blogapi.payload.AlbumResponse;
import com.sopromadze.blogapi.payload.ApiResponse;
import com.sopromadze.blogapi.payload.PagedResponse;
-import com.sopromadze.blogapi.repository.AlbumRepository;
-import com.sopromadze.blogapi.repository.UserRepository;
+import com.sopromadze.blogapi.payload.request.AlbumRequest;
import com.sopromadze.blogapi.security.UserPrincipal;
-import com.sopromadze.blogapi.util.AppConstants;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
-import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.stereotype.Service;
-import java.util.Collections;
+public interface AlbumService {
-@Service
-public class AlbumService {
- private final AlbumRepository albumRepository;
- private final UserRepository userRepository;
+ PagedResponse getAllAlbums(int page, int size);
- @Autowired
- public AlbumService(AlbumRepository albumRepository, UserRepository userRepository) {
- this.albumRepository = albumRepository;
- this.userRepository = userRepository;
- }
+ ResponseEntity addAlbum(AlbumRequest albumRequest, UserPrincipal currentUser);
- public PagedResponse getAllAlbums(int page, int size){
- validatePageNumberAndSize(page, size);
+ ResponseEntity getAlbum(Long id);
- Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt");
+ ResponseEntity updateAlbum(Long id, AlbumRequest newAlbum, UserPrincipal currentUser);
- Page albums = albumRepository.findAll(pageable);
+ ResponseEntity deleteAlbum(Long id, UserPrincipal currentUser);
- if (albums.getNumberOfElements() == 0){
- return new PagedResponse<>(Collections.emptyList(), albums.getNumber(), albums.getSize(), albums.getTotalElements(), albums.getTotalPages(), albums.isLast());
- }
- return new PagedResponse<>(albums.getContent(), albums.getNumber(), albums.getSize(), albums.getTotalElements(), albums.getTotalPages(), albums.isLast());
- }
+ PagedResponse getUserAlbums(String username, int page, int size);
- public ResponseEntity> addAlbum(Album album, UserPrincipal currentUser){
- User user = userRepository.findByUsername(currentUser.getUsername()).orElseThrow(() -> new ResourceNotFoundException("User", "username", currentUser.getUsername()));
- album.setUser(user);
- Album newAlbum = albumRepository.save(album);
- return new ResponseEntity<>(newAlbum, HttpStatus.CREATED);
- }
-
- public ResponseEntity> getAlbum(Long id){
- Album album = albumRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Album", "id", id));
- return new ResponseEntity<>(album, HttpStatus.OK);
- }
-
- public ResponseEntity> updateAlbum(Long id, Album newAlbum, UserPrincipal currentUser){
- Album album = albumRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Album", "id", id));
- User user = userRepository.findByUsername(currentUser.getUsername()).orElseThrow(() -> new ResourceNotFoundException("User", "username", currentUser.getUsername()));
- if (album.getUser().getId().equals(user.getId()) || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))){
- album.setTitle(newAlbum.getTitle());
- Album updatedAlbum = albumRepository.save(album);
- return new ResponseEntity<>(updatedAlbum, HttpStatus.OK);
- }
- return new ResponseEntity<>(new ApiResponse(false, "You don't have permission to make this operation"), HttpStatus.UNAUTHORIZED);
- }
-
- public ResponseEntity> deleteAlbum(Long id, UserPrincipal currentUser){
- Album album = albumRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Album", "id", id));
- User user = userRepository.findByUsername(currentUser.getUsername()).orElseThrow(() -> new ResourceNotFoundException("User", "username", currentUser.getUsername()));
- if (album.getUser().getId().equals(user.getId()) || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))){
- albumRepository.deleteById(id);
- return new ResponseEntity<>(new ApiResponse(true, "You successfully deleted album"), HttpStatus.OK);
- }
- return new ResponseEntity<>(new ApiResponse(false, "You don't have permission to make this operation"), HttpStatus.UNAUTHORIZED);
- }
-
- public PagedResponse getUserAlbums(String username, int page, int size){
- User user = userRepository.findByUsername(username).orElseThrow(() -> new ResourceNotFoundException("User", "username", username));
- Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt");
-
- Page albums = albumRepository.findByCreatedBy(user.getId(), pageable);
-
- if (albums.getNumberOfElements() == 0){
- return new PagedResponse<>(Collections.emptyList(), albums.getNumber(), albums.getSize(), albums.getTotalElements(), albums.getTotalPages(), albums.isLast());
- }
- return new PagedResponse<>(albums.getContent(), albums.getNumber(), albums.getSize(), albums.getTotalElements(), albums.getTotalPages(), albums.isLast());
- }
-
- private void validatePageNumberAndSize(int page, int size) {
- if(page < 0) {
- throw new BadRequestException("Page number cannot be less than zero.");
- }
-
- if(size < 0) {
- throw new BadRequestException("Size number cannot be less than zero.");
- }
-
- if(size > AppConstants.MAX_PAGE_SIZE) {
- throw new BadRequestException("Page size must not be greater than " + AppConstants.MAX_PAGE_SIZE);
- }
- }
}
diff --git a/src/main/java/com/sopromadze/blogapi/service/CategoryService.java b/src/main/java/com/sopromadze/blogapi/service/CategoryService.java
new file mode 100644
index 00000000..65734a3e
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/service/CategoryService.java
@@ -0,0 +1,23 @@
+package com.sopromadze.blogapi.service;
+
+import com.sopromadze.blogapi.exception.UnauthorizedException;
+import com.sopromadze.blogapi.model.Category;
+import com.sopromadze.blogapi.payload.ApiResponse;
+import com.sopromadze.blogapi.payload.PagedResponse;
+import com.sopromadze.blogapi.security.UserPrincipal;
+import org.springframework.http.ResponseEntity;
+
+public interface CategoryService {
+
+ PagedResponse getAllCategories(int page, int size);
+
+ ResponseEntity getCategory(Long id);
+
+ ResponseEntity addCategory(Category category, UserPrincipal currentUser);
+
+ ResponseEntity updateCategory(Long id, Category newCategory, UserPrincipal currentUser)
+ throws UnauthorizedException;
+
+ ResponseEntity deleteCategory(Long id, UserPrincipal currentUser) throws UnauthorizedException;
+
+}
diff --git a/src/main/java/com/sopromadze/blogapi/service/CommentService.java b/src/main/java/com/sopromadze/blogapi/service/CommentService.java
index 1940b969..cdbc34de 100644
--- a/src/main/java/com/sopromadze/blogapi/service/CommentService.java
+++ b/src/main/java/com/sopromadze/blogapi/service/CommentService.java
@@ -1,117 +1,21 @@
package com.sopromadze.blogapi.service;
-import com.sopromadze.blogapi.exception.BadRequestException;
-import com.sopromadze.blogapi.exception.ResourceNotFoundException;
-import com.sopromadze.blogapi.model.comment.Comment;
-import com.sopromadze.blogapi.model.post.Post;
-import com.sopromadze.blogapi.model.role.RoleName;
-import com.sopromadze.blogapi.model.user.User;
+import com.sopromadze.blogapi.model.Comment;
import com.sopromadze.blogapi.payload.ApiResponse;
import com.sopromadze.blogapi.payload.CommentRequest;
import com.sopromadze.blogapi.payload.PagedResponse;
-import com.sopromadze.blogapi.repository.CommentRepository;
-import com.sopromadze.blogapi.repository.PostRepository;
-import com.sopromadze.blogapi.repository.UserRepository;
import com.sopromadze.blogapi.security.UserPrincipal;
-import com.sopromadze.blogapi.util.AppConstants;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.stereotype.Service;
-@Service
-public class CommentService {
- private final CommentRepository commentRepository;
- private final PostRepository postRepository;
- private final UserRepository userRepository;
+public interface CommentService {
- @Autowired
- public CommentService(CommentRepository commentRepository, PostRepository postRepository, UserRepository userRepository) {
- this.commentRepository = commentRepository;
- this.postRepository = postRepository;
- this.userRepository = userRepository;
- }
+ PagedResponse getAllComments(Long postId, int page, int size);
- public PagedResponse> getAllComments(Long postId, int page, int size){
- validatePageNumberAndSize(page, size);
- Post post = postRepository.findById(postId).orElseThrow(() -> new ResourceNotFoundException("Post", "id", postId));
+ Comment addComment(CommentRequest commentRequest, Long postId, UserPrincipal currentUser);
- Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt");
+ Comment getComment(Long postId, Long id);
- Page comments = commentRepository.findByPostId(postId, pageable);
+ Comment updateComment(Long postId, Long id, CommentRequest commentRequest, UserPrincipal currentUser);
- return new PagedResponse<>(comments.getContent(), comments.getNumber(), comments.getSize(), comments.getTotalElements(), comments.getTotalPages(), comments.isLast());
- }
+ ApiResponse deleteComment(Long postId, Long id, UserPrincipal currentUser);
- public ResponseEntity> addComment(CommentRequest commentRequest, Long postId, UserPrincipal currentUser){
- Post post = postRepository.findById(postId).orElseThrow(() -> new ResourceNotFoundException("Post", "id", postId));
- User user = userRepository.findByUsername(currentUser.getUsername()).orElseThrow(() -> new ResourceNotFoundException("User", "username", currentUser.getUsername()));
- Comment comment = new Comment(commentRequest.getBody());
- comment.setUser(user);
- comment.setPost(post);
- comment.setName(currentUser.getUsername());
- comment.setEmail(currentUser.getEmail());
- Comment newComment = commentRepository.save(comment);
- return new ResponseEntity<>(newComment, HttpStatus.CREATED);
- }
-
- public ResponseEntity> getComment(Long postId, Long id){
- Post post = postRepository.findById(postId).orElseThrow(() -> new ResourceNotFoundException("Post", "id", postId));
- Comment comment = commentRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Comment", "id", id));
- if (comment.getPost().getId().equals(post.getId())){
- return new ResponseEntity<>(comment, HttpStatus.OK);
- }
- return new ResponseEntity<>(new ApiResponse(false, "Comment does not belong to post"), HttpStatus.BAD_REQUEST);
- }
-
- public ResponseEntity> updateComment(Long postId, Long id, CommentRequest commentRequest, UserPrincipal currentUser){
- Post post = postRepository.findById(postId).orElseThrow(() -> new ResourceNotFoundException("Post", "id", postId));
- Comment comment = commentRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Comment", "id", id));
-
- if (!comment.getPost().getId().equals(post.getId())){
- return new ResponseEntity<>(new ApiResponse(false, "Comment does not belong to post"), HttpStatus.BAD_REQUEST);
- }
-
- if (comment.getUser().getId().equals(currentUser.getId()) || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))){
- comment.setBody(commentRequest.getBody());
- Comment updatedComment = commentRepository.save(comment);
- return new ResponseEntity<>(updatedComment, HttpStatus.OK);
- }
- return new ResponseEntity<>(new ApiResponse(false, "You don't have permission to update this comment"), HttpStatus.UNAUTHORIZED);
- }
-
- public ResponseEntity> deleteComment(Long postId, Long id, UserPrincipal currentUser){
- Post post = postRepository.findById(postId).orElseThrow(() -> new ResourceNotFoundException("Post", "id", postId));
- Comment comment = commentRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Comment", "id", id));
-
- if (!comment.getPost().getId().equals(post.getId())){
- return new ResponseEntity<>(new ApiResponse(false, "Comment does not belong to post"), HttpStatus.BAD_REQUEST);
- }
-
- if (comment.getUser().getId().equals(currentUser.getId()) || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))){
- commentRepository.deleteById(comment.getId());
- return new ResponseEntity<>(new ApiResponse(true, "You successfully deleted comment"), HttpStatus.OK);
- }
-
- return new ResponseEntity<>(new ApiResponse(false, "You don't have permission to delete this comment"), HttpStatus.BAD_REQUEST);
- }
-
- private void validatePageNumberAndSize(int page, int size) {
- if(page < 0) {
- throw new BadRequestException("Page number cannot be less than zero.");
- }
-
- if(size < 0) {
- throw new BadRequestException("Size number cannot be less than zero.");
- }
-
- if(size > AppConstants.MAX_PAGE_SIZE) {
- throw new BadRequestException("Page size must not be greater than " + AppConstants.MAX_PAGE_SIZE);
- }
- }
}
diff --git a/src/main/java/com/sopromadze/blogapi/service/CustomUserDetailsService.java b/src/main/java/com/sopromadze/blogapi/service/CustomUserDetailsService.java
index 357b880b..55084feb 100644
--- a/src/main/java/com/sopromadze/blogapi/service/CustomUserDetailsService.java
+++ b/src/main/java/com/sopromadze/blogapi/service/CustomUserDetailsService.java
@@ -1,36 +1,12 @@
package com.sopromadze.blogapi.service;
-import com.sopromadze.blogapi.model.user.User;
-import com.sopromadze.blogapi.repository.UserRepository;
-import com.sopromadze.blogapi.security.UserPrincipal;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
-import org.springframework.stereotype.Service;
-import javax.transaction.Transactional;
+public interface CustomUserDetailsService {
-@Service
-public class CustomUserDetailsService implements UserDetailsService {
- private final UserRepository userRepository;
+ UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException;
- @Autowired
- public CustomUserDetailsService(UserRepository userRepository) {
- this.userRepository = userRepository;
- }
+ UserDetails loadUserById(Long id);
- @Override
- @Transactional
- public UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException {
- User user = userRepository.findByUsernameOrEmail(usernameOrEmail, usernameOrEmail).orElseThrow(() -> new UsernameNotFoundException(String.format("User not found with this username or email: %s", usernameOrEmail)));
- return UserPrincipal.create(user);
- }
-
- @Transactional
- public UserDetails loadUserById(Long id){
- User user = userRepository.findById(id).orElseThrow(() -> new UsernameNotFoundException(String.format("User not found with id: %s", id)));
-
- return UserPrincipal.create(user);
- }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/sopromadze/blogapi/service/PhotoService.java b/src/main/java/com/sopromadze/blogapi/service/PhotoService.java
index 5238b2a1..b59c767f 100644
--- a/src/main/java/com/sopromadze/blogapi/service/PhotoService.java
+++ b/src/main/java/com/sopromadze/blogapi/service/PhotoService.java
@@ -1,125 +1,23 @@
package com.sopromadze.blogapi.service;
-import com.sopromadze.blogapi.exception.BadRequestException;
-import com.sopromadze.blogapi.exception.ResourceNotFoundException;
-import com.sopromadze.blogapi.model.album.Album;
-import com.sopromadze.blogapi.model.photo.Photo;
-import com.sopromadze.blogapi.model.role.RoleName;
import com.sopromadze.blogapi.payload.ApiResponse;
import com.sopromadze.blogapi.payload.PagedResponse;
import com.sopromadze.blogapi.payload.PhotoRequest;
import com.sopromadze.blogapi.payload.PhotoResponse;
-import com.sopromadze.blogapi.repository.AlbumRepository;
-import com.sopromadze.blogapi.repository.PhotoRepository;
import com.sopromadze.blogapi.security.UserPrincipal;
-import com.sopromadze.blogapi.util.AppConstants;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.stereotype.Service;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+public interface PhotoService {
-@Service
-public class PhotoService {
- private final PhotoRepository photoRepository;
- private final AlbumRepository albumRepository;
+ PagedResponse getAllPhotos(int page, int size);
- @Autowired
- public PhotoService(PhotoRepository photoRepository, AlbumRepository albumRepository) {
- this.photoRepository = photoRepository;
- this.albumRepository = albumRepository;
- }
+ PhotoResponse getPhoto(Long id);
- public PagedResponse getAllPhotos(int page, int size){
- validatePageNumberAndSize(page, size);
+ PhotoResponse updatePhoto(Long id, PhotoRequest photoRequest, UserPrincipal currentUser);
- Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt");
- Page photos = photoRepository.findAll(pageable);
+ PhotoResponse addPhoto(PhotoRequest photoRequest, UserPrincipal currentUser);
- List photoResponses = new ArrayList<>();
- for (Photo photo : photos.getContent()){
- photoResponses.add(new PhotoResponse(photo.getId(), photo.getTitle(), photo.getUrl(), photo.getThumbnailUrl(), photo.getAlbum().getId()));
- }
+ ApiResponse deletePhoto(Long id, UserPrincipal currentUser);
- if (photos.getNumberOfElements() == 0){
- return new PagedResponse<>(Collections.emptyList(), photos.getNumber(), photos.getSize(), photos.getTotalElements(), photos.getTotalPages(), photos.isLast());
- }
- return new PagedResponse<>(photoResponses, photos.getNumber(), photos.getSize(), photos.getTotalElements(), photos.getTotalPages(), photos.isLast());
+ PagedResponse getAllPhotosByAlbum(Long albumId, int page, int size);
- }
-
- public ResponseEntity> getPhoto(Long id){
- Photo photo = photoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Photo", "id", id));
- return new ResponseEntity<>(new PhotoResponse(photo.getId(), photo.getTitle(), photo.getUrl(), photo.getThumbnailUrl(), photo.getAlbum().getId()), HttpStatus.OK);
- }
-
- public ResponseEntity> updatePhoto(Long id, PhotoRequest photoRequest, UserPrincipal currentUser){
- Album album = albumRepository.findById(photoRequest.getAlbumId()).orElseThrow(() -> new ResourceNotFoundException("Album", "id", photoRequest.getAlbumId()));
- Photo photo = photoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Photo", "id", id));
- if(photo.getAlbum().getUser().getId().equals(currentUser.getId()) || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))){
- photo.setTitle(photoRequest.getTitle());
- photo.setThumbnailUrl(photoRequest.getThumbnailUrl());
- photo.setAlbum(album);
- Photo updatedPhoto = photoRepository.save(photo);
- return new ResponseEntity<>(new PhotoResponse(updatedPhoto.getId(), updatedPhoto.getTitle(), updatedPhoto.getUrl(), updatedPhoto.getThumbnailUrl(), updatedPhoto.getAlbum().getId()), HttpStatus.OK);
- }
- return new ResponseEntity<>(new ApiResponse(false, "You don't have permission to update this photo"), HttpStatus.UNAUTHORIZED);
- }
-
- public ResponseEntity> addPhoto(PhotoRequest photoRequest, UserPrincipal currentUser){
- Album album = albumRepository.findById(photoRequest.getAlbumId()).orElseThrow(() -> new ResourceNotFoundException("Album", "id", photoRequest.getAlbumId()));
- if (album.getUser().getId().equals(currentUser.getId())){
- Photo photo = new Photo(photoRequest.getTitle(), photoRequest.getUrl(), photoRequest.getThumbnailUrl(), album);
- Photo newPhoto = photoRepository.save(photo);
- return new ResponseEntity<>(new PhotoResponse(newPhoto.getId(), newPhoto.getTitle(), newPhoto.getUrl(), newPhoto.getThumbnailUrl(), newPhoto.getAlbum().getId()), HttpStatus.CREATED);
- }
- return new ResponseEntity<>(new ApiResponse(false, "You don't have permission to add photo in this album"), HttpStatus.UNAUTHORIZED);
- }
-
- public ResponseEntity> deletePhoto(Long id, UserPrincipal currentUser){
- Photo photo = photoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Photo", "id", id));
- if(photo.getAlbum().getUser().getId().equals(currentUser.getId()) || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))){
- photoRepository.deleteById(id);
- return new ResponseEntity<>(new ApiResponse(true, "Photo deleted successfully"), HttpStatus.OK);
- }
- return new ResponseEntity<>(new ApiResponse(false, "You don't have permission to delete this photo"), HttpStatus.UNAUTHORIZED);
- }
-
- public PagedResponse> getAllPhotosByAlbum(Long albumId, int page, int size){
- Album album = albumRepository.findById(albumId).orElseThrow(() -> new ResourceNotFoundException("Album", "id", albumId));
- validatePageNumberAndSize(page, size);
-
- Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt");
-
- Page photos = photoRepository.findByAlbumId(albumId, pageable);
-
- List photoResponses = new ArrayList<>();
- for(Photo photo : photos.getContent()){
- photoResponses.add(new PhotoResponse(photo.getId(), photo.getTitle(), photo.getUrl(), photo.getThumbnailUrl(), photo.getAlbum().getId()));
- }
-
- return new PagedResponse<>(photoResponses, photos.getNumber(), photos.getSize(), photos.getTotalElements(), photos.getTotalPages(), photos.isLast());
- }
-
- private void validatePageNumberAndSize(int page, int size) {
- if(page < 0) {
- throw new BadRequestException("Page number cannot be less than zero.");
- }
-
- if(size < 0) {
- throw new BadRequestException("Size number cannot be less than zero.");
- }
-
- if(size > AppConstants.MAX_PAGE_SIZE) {
- throw new BadRequestException("Page size must not be greater than " + AppConstants.MAX_PAGE_SIZE);
- }
- }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/sopromadze/blogapi/service/PostService.java b/src/main/java/com/sopromadze/blogapi/service/PostService.java
index 115c6554..d4754205 100644
--- a/src/main/java/com/sopromadze/blogapi/service/PostService.java
+++ b/src/main/java/com/sopromadze/blogapi/service/PostService.java
@@ -1,113 +1,28 @@
package com.sopromadze.blogapi.service;
-import com.sopromadze.blogapi.exception.BadRequestException;
-import com.sopromadze.blogapi.exception.ResourceNotFoundException;
-import com.sopromadze.blogapi.model.post.Post;
-import com.sopromadze.blogapi.model.role.RoleName;
-import com.sopromadze.blogapi.model.user.User;
+import com.sopromadze.blogapi.model.Post;
import com.sopromadze.blogapi.payload.ApiResponse;
import com.sopromadze.blogapi.payload.PagedResponse;
-import com.sopromadze.blogapi.repository.PostRepository;
-import com.sopromadze.blogapi.repository.UserRepository;
+import com.sopromadze.blogapi.payload.PostRequest;
+import com.sopromadze.blogapi.payload.PostResponse;
import com.sopromadze.blogapi.security.UserPrincipal;
-import com.sopromadze.blogapi.util.AppConstants;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.stereotype.Service;
-import java.util.Collections;
+public interface PostService {
-@Service
-public class PostService {
- private final PostRepository postRepository;
+ PagedResponse getAllPosts(int page, int size);
- private final UserRepository userRepository;
+ PagedResponse getPostsByCreatedBy(String username, int page, int size);
- private static final Logger logger = LoggerFactory.getLogger(PostService.class);
+ PagedResponse getPostsByCategory(Long id, int page, int size);
- @Autowired
- public PostService(PostRepository postRepository, UserRepository userRepository) {
- this.postRepository = postRepository;
- this.userRepository = userRepository;
- }
+ PagedResponse getPostsByTag(Long id, int page, int size);
- public PagedResponse getAllPosts(int page, int size){
- validatePageNumberAndSize(page, size);
+ Post updatePost(Long id, PostRequest newPostRequest, UserPrincipal currentUser);
- Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt");
+ ApiResponse deletePost(Long id, UserPrincipal currentUser);
- Page posts = postRepository.findAll(pageable);
+ PostResponse addPost(PostRequest postRequest, UserPrincipal currentUser);
- if (posts.getNumberOfElements() == 0){
- return new PagedResponse<>(Collections.emptyList(), posts.getNumber(), posts.getSize(), posts.getTotalElements(), posts.getTotalPages(), posts.isLast());
- }
+ Post getPost(Long id);
- return new PagedResponse<>(posts.getContent(), posts.getNumber(), posts.getSize(), posts.getTotalElements(), posts.getTotalPages(), posts.isLast());
- }
-
- public PagedResponse getPostsCreatedBy(String username, int page, int size){
- validatePageNumberAndSize(page, size);
- User user = userRepository.findByUsername(username).orElseThrow(() -> new ResourceNotFoundException("User", "username", username));
- Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt");
- Page posts = postRepository.findByCreatedBy(user.getId(), pageable);
-
- if(posts.getNumberOfElements() == 0){
- return new PagedResponse<>(Collections.emptyList(), posts.getNumber(), posts.getSize(), posts.getTotalElements(), posts.getTotalPages(), posts.isLast());
- }
- return new PagedResponse<>(posts.getContent(), posts.getNumber(), posts.getSize(), posts.getTotalElements(), posts.getTotalPages(), posts.isLast());
- }
-
- public ResponseEntity> updatePost(Long id, Post newPost, UserPrincipal currentUser){
- Post post = postRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Post", "id", id));
- if (post.getUser().getId().equals(currentUser.getId()) || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))){
- post.setTitle(newPost.getTitle());
- post.setBody(newPost.getBody());
- Post updatedPost = postRepository.save(post);
- return new ResponseEntity<>(updatedPost, HttpStatus.OK);
- }
- return new ResponseEntity<>(new ApiResponse(false, "You don't have permission to edit this post"), HttpStatus.UNAUTHORIZED);
- }
-
- public ResponseEntity> deletePost(Long id, UserPrincipal currentUser){
- Post post = postRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Post", "id", id));
- if (post.getUser().getId().equals(currentUser.getId()) || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))){
- postRepository.deleteById(id);
- return new ResponseEntity<>(new ApiResponse(true, "You successfully deleted post"), HttpStatus.OK);
- }
- return new ResponseEntity<>(new ApiResponse(true, "You don't have permission to delete this post"), HttpStatus.UNAUTHORIZED);
- }
-
- public ResponseEntity> addPost(Post post, UserPrincipal currentUser){
- User user = userRepository.findById(currentUser.getId()).orElseThrow(() -> new ResourceNotFoundException("User", "id", 1L));
- post.setUser(user);
- Post newPost = postRepository.save(post);
- return new ResponseEntity<>(newPost, HttpStatus.CREATED);
- }
-
- public ResponseEntity> getPost(Long id){
- Post post = postRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Post", "id", id));
- return new ResponseEntity<>(post, HttpStatus.OK);
- }
-
- private void validatePageNumberAndSize(int page, int size) {
- if(page < 0) {
- throw new BadRequestException("Page number cannot be less than zero.");
- }
-
- if(size < 0) {
- throw new BadRequestException("Size number cannot be less than zero.");
- }
-
- if(size > AppConstants.MAX_PAGE_SIZE) {
- throw new BadRequestException("Page size must not be greater than " + AppConstants.MAX_PAGE_SIZE);
- }
- }
}
diff --git a/src/main/java/com/sopromadze/blogapi/service/TagService.java b/src/main/java/com/sopromadze/blogapi/service/TagService.java
new file mode 100644
index 00000000..db321cca
--- /dev/null
+++ b/src/main/java/com/sopromadze/blogapi/service/TagService.java
@@ -0,0 +1,20 @@
+package com.sopromadze.blogapi.service;
+
+import com.sopromadze.blogapi.model.Tag;
+import com.sopromadze.blogapi.payload.ApiResponse;
+import com.sopromadze.blogapi.payload.PagedResponse;
+import com.sopromadze.blogapi.security.UserPrincipal;
+
+public interface TagService {
+
+ PagedResponse getAllTags(int page, int size);
+
+ Tag getTag(Long id);
+
+ Tag addTag(Tag tag, UserPrincipal currentUser);
+
+ Tag updateTag(Long id, Tag newTag, UserPrincipal currentUser);
+
+ ApiResponse deleteTag(Long id, UserPrincipal currentUser);
+
+}
diff --git a/src/main/java/com/sopromadze/blogapi/service/TodoService.java b/src/main/java/com/sopromadze/blogapi/service/TodoService.java
index e02a6a46..23e7903d 100644
--- a/src/main/java/com/sopromadze/blogapi/service/TodoService.java
+++ b/src/main/java/com/sopromadze/blogapi/service/TodoService.java
@@ -1,121 +1,24 @@
package com.sopromadze.blogapi.service;
-import com.sopromadze.blogapi.exception.BadRequestException;
-import com.sopromadze.blogapi.exception.ResourceNotFoundException;
-import com.sopromadze.blogapi.model.todo.Todo;
-import com.sopromadze.blogapi.model.user.User;
+import com.sopromadze.blogapi.model.Todo;
import com.sopromadze.blogapi.payload.ApiResponse;
import com.sopromadze.blogapi.payload.PagedResponse;
-import com.sopromadze.blogapi.repository.TodoRepository;
-import com.sopromadze.blogapi.repository.UserRepository;
import com.sopromadze.blogapi.security.UserPrincipal;
-import com.sopromadze.blogapi.util.AppConstants;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Service;
-import java.util.Collections;
+public interface TodoService {
-@Service
-public class TodoService {
- private final TodoRepository todoRepository;
- private final UserRepository userRepository;
+ Todo completeTodo(Long id, UserPrincipal currentUser);
- @Autowired
- public TodoService(TodoRepository todoRepository, UserRepository userRepository) {
- this.todoRepository = todoRepository;
- this.userRepository = userRepository;
- }
+ Todo unCompleteTodo(Long id, UserPrincipal currentUser);
- public ResponseEntity> completeTodo(Long id, UserPrincipal currentUser) {
- Todo todo = todoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Todo", "id", id));
- User user = userRepository.findByUsername(currentUser.getUsername()).orElseThrow(() -> new ResourceNotFoundException("User", "username", currentUser.getUsername()));
- if(todo.getUser().getId().equals(user.getId())){
- todo.setCompleted(true);
- Todo result = todoRepository.save(todo);
- return new ResponseEntity<>(todo, HttpStatus.OK);
- }
- return todo.getUser().getId().equals(user.getId()) ? new ResponseEntity<>(todo, HttpStatus.OK) : new ResponseEntity<>(new ApiResponse(false, "You don't have permission to make this operation"), HttpStatus.UNAUTHORIZED);
- }
+ PagedResponse