From f94ffd18510ca8d0e67a18aa5810abcb652a9b93 Mon Sep 17 00:00:00 2001 From: Omari Sopromadze Date: Sat, 11 May 2024 00:48:41 +0200 Subject: [PATCH 1/3] feat: add postgresql --- Dockerfile | 5 - data/blogapi.sql | 192 ----------------- docker-compose.yml | 36 ---- docker/docker-compose.yml | 15 ++ docker/init.sql | 175 +++++++++++++++ pom.xml | 240 ++++++++++----------- src/main/resources/_application.properties | 12 -- src/main/resources/application.yml | 8 +- src/main/resources/blogapi.sql | 190 ---------------- 9 files changed, 314 insertions(+), 559 deletions(-) delete mode 100644 Dockerfile delete mode 100644 data/blogapi.sql delete mode 100644 docker-compose.yml create mode 100644 docker/docker-compose.yml create mode 100644 docker/init.sql delete mode 100644 src/main/resources/_application.properties delete mode 100644 src/main/resources/blogapi.sql diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index e7d65cce..00000000 --- a/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -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/data/blogapi.sql b/data/blogapi.sql deleted file mode 100644 index 5cc9ced8..00000000 --- a/data/blogapi.sql +++ /dev/null @@ -1,192 +0,0 @@ -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 deleted file mode 100644 index 9bd9053e..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,36 +0,0 @@ -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/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 00000000..bff7f5e0 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,15 @@ +version: '3.8' +services: + db: + image: 'postgres:16.3' + container_name: postgres_db + restart: always + ports: + - "5432:5432" + environment: + POSTGRES_DB: blogapi + POSTGRES_USER: blogapi_user + POSTGRES_PASSWORD: blogapi_pass + POSTGRES_ROOT_PASSWORD: blogapi_root_pass + volumes: + - ./init.sql:/docker-entrypoint-initdb.d/init.sql diff --git a/docker/init.sql b/docker/init.sql new file mode 100644 index 00000000..03c0cc16 --- /dev/null +++ b/docker/init.sql @@ -0,0 +1,175 @@ +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 IF NOT EXISTS tags +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + name varchar(255) NOT NULL, + created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by bigint check (created_by > 0) NOT NULL, + updated_by bigint check (updated_by > 0) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE IF NOT EXISTS geo +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + lat varchar(255), + lng varchar(255), + created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by bigint check (created_by > 0) NOT NULL, + updated_by bigint check (updated_by > 0) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE IF NOT EXISTS company +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + name varchar(255), + catch_phrase varchar(255), + bs varchar(255), + created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by bigint check (created_by > 0) NOT NULL, + updated_by bigint check (updated_by > 0) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE IF NOT EXISTS address +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + street varchar(255), + suite varchar(255), + city varchar(255), + zipcode varchar(255), + geo_id bigint check (geo_id > 0) NOT NULL REFERENCES geo (id), + created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by bigint check (created_by > 0) NOT NULL, + updated_by bigint check (updated_by > 0) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE IF NOT EXISTS users +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + 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, + phone varchar(255), + website varchar(255), + address_id bigint check (address_id > 0) DEFAULT NULL REFERENCES address (id), + company_id bigint check (company_id > 0) DEFAULT NULL REFERENCES company (id), + created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id) +); + +CREATE TABLE IF NOT EXISTS todos +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + title varchar(255) NOT NULL, + completed boolean default false, + user_id bigint check (user_id > 0) DEFAULT NULL REFERENCES users (id), + created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by bigint check (created_by > 0) NOT NULL, + updated_by bigint check (updated_by > 0) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE IF NOT EXISTS albums +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + title varchar(255) NOT NULL, + user_id bigint check (user_id > 0) DEFAULT NULL REFERENCES users (id), + created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by bigint check (created_by > 0) NOT NULL, + updated_by bigint check (updated_by > 0) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE photos +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + title varchar(255) NOT NULL, + url varchar(255) NOT NULL, + thumbnail_url varchar(255) NOT NULL, + album_id bigint check (album_id > 0) DEFAULT NULL REFERENCES albums (id), + created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by bigint check (created_by > 0) NOT NULL, + updated_by bigint check (updated_by > 0) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE posts +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + title varchar(255) NOT NULL, + body text NOT NULL, + user_id bigint check (user_id > 0) DEFAULT NULL REFERENCES users (id), + created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by bigint check (created_by > 0) NOT NULL, + updated_by bigint check (updated_by > 0) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE post_tag +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + post_id bigint check (post_id > 0) NOT NULL REFERENCES posts (id), + tag_id bigint check ( tag_id > 0 ) NOT NULL REFERENCES tags (id), + PRIMARY KEY (id) +); + +CREATE TABLE comments +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + name varchar(255) NOT NULL, + email varchar(255) NOT NULL, + body text NOT NULL, + post_id bigint check ( post_id > 0 ) DEFAULT NULL REFERENCES posts (id), + user_id bigint check ( user_id > 0 ) DEFAULT NULL REFERENCES users (id), + created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by bigint check (created_by > 0) NOT NULL, + updated_by bigint check (updated_by > 0) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE roles +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + name varchar(255) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE user_role +( + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + user_id bigint check ( user_id > 0 ) NOT NULL REFERENCES users (id), + role_id bigint check ( role_id > 0 ) NOT NULL REFERENCES roles (id), + PRIMARY KEY (id) +); + +INSERT INTO roles(name) +VALUES ('ROLE_ADMIN'), + ('ROLE_USER'); diff --git a/pom.xml b/pom.xml index 4b6fd187..0b7dd2c5 100644 --- a/pom.xml +++ b/pom.xml @@ -23,8 +23,8 @@ UTF-8 UTF-8 1.8 - coma123 - https://sonarcloud.io + coma123 + https://sonarcloud.io @@ -34,131 +34,131 @@ - mysql - mysql-connector-java + org.postgresql + postgresql runtime - - org.projectlombok - lombok - 1.18.12 - provided - + + org.projectlombok + lombok + 1.18.30 + provided + - - org.springframework.boot - spring-boot-starter-web - + + org.springframework.boot + spring-boot-starter-web + org.springframework.boot spring-boot-starter-aop - - - - 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 - - - - - - + + + + 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/src/main/resources/_application.properties b/src/main/resources/_application.properties deleted file mode 100644 index d7d7b14c..00000000 --- a/src/main/resources/_application.properties +++ /dev/null @@ -1,12 +0,0 @@ -spring.jpa.hibernate.ddl-auto=none -spring.datasource.url=jdbc:mysql://localhost:3306/blogapi?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true -spring.datasource.username=root -spring.datasource.password=root -spring.jpa.show-sql=true -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect -app.jwtSecret=secret -# Expiration in milliseconds - 1 Hour -app.jwtExpirationInMs=3600000 -spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false -spring.jackson.time-zone=UTC -cors.allowedOrings=* diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ef6d7e3d..8b64d3c1 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,15 +1,15 @@ spring: datasource: - url: jdbc:mysql://blogapi-db:3306/blogapi?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true - username: root - password: root + url: jdbc:postgresql://localhost:5432/blogapi + username: postgres + password: postgres jpa: hibernate: ddl-auto: none show-sql: true properties: hibernate: - dialect: org.hibernate.dialect.MySQL5Dialect + dialect: org.hibernate.dialect.PostgreSQLDialect jackson: serialization: WRITE_DATES_AS_TIMESTAMPS: false diff --git a/src/main/resources/blogapi.sql b/src/main/resources/blogapi.sql deleted file mode 100644 index 3a85baec..00000000 --- a/src/main/resources/blogapi.sql +++ /dev/null @@ -1,190 +0,0 @@ -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; From 7a449fdd078ff40393e5d19ccb251f7b30e6123a Mon Sep 17 00:00:00 2001 From: Omari Sopromadze Date: Sat, 11 May 2024 02:26:03 +0200 Subject: [PATCH 2/3] feat: migrate to java 21 and spring boot 3.2.5 --- pom.xml | 44 ++- .../blogapi/BlogApiApplication.java | 32 +- .../config/ApplicationConfiguration.java | 45 +++ .../config/JwtAuthenticationFilter.java | 69 ++++ .../blogapi/config/SecurityConfig.java | 59 ++++ .../blogapi/config/SecutiryConfig.java | 76 ----- .../blogapi/controller/AlbumController.java | 86 ++--- .../blogapi/controller/AuthController.java | 108 ------ .../controller/AuthenticationController.java | 47 +++ .../controller/CategoryController.java | 62 ++-- .../blogapi/controller/CommentController.java | 78 ++--- .../blogapi/controller/PhotoController.java | 68 ++-- .../blogapi/controller/PostController.java | 130 ++++---- .../blogapi/controller/TagController.java | 66 ++-- .../blogapi/controller/TodoController.java | 98 +++--- .../blogapi/controller/UserController.java | 162 ++++----- .../com/sopromadze/blogapi/model/Album.java | 81 ++--- .../sopromadze/blogapi/model/Category.java | 73 ++-- .../com/sopromadze/blogapi/model/Comment.java | 24 +- .../com/sopromadze/blogapi/model/Photo.java | 90 ++--- .../com/sopromadze/blogapi/model/Post.java | 151 ++++----- .../com/sopromadze/blogapi/model/Tag.java | 60 ++-- .../com/sopromadze/blogapi/model/Todo.java | 56 ++-- .../blogapi/model/audit/DateAudit.java | 25 +- .../blogapi/model/audit/UserDateAudit.java | 20 +- .../sopromadze/blogapi/model/role/Role.java | 36 +- .../blogapi/model/user/Address.java | 181 +++++----- .../blogapi/model/user/Company.java | 177 +++++----- .../sopromadze/blogapi/model/user/Geo.java | 151 ++++----- .../sopromadze/blogapi/model/user/User.java | 312 +++++++++--------- .../blogapi/payload/CommentRequest.java | 10 +- .../blogapi/payload/InfoRequest.java | 32 +- .../blogapi/payload/LoginRequest.java | 10 +- .../blogapi/payload/LoginResponse.java | 16 + .../blogapi/payload/LoginUserDto.java | 15 + .../blogapi/payload/PhotoRequest.java | 28 +- .../blogapi/payload/PostRequest.java | 45 +-- .../blogapi/payload/RegisterUserDto.java | 17 + .../blogapi/payload/SignUpRequest.java | 38 +-- .../blogapi/repository/UserRepository.java | 27 +- .../security/JwtAuthenticationEntryPoint.java | 24 -- .../security/JwtAuthenticationFilter.java | 57 ---- .../blogapi/security/JwtTokenProvider.java | 68 ---- .../service/AuthenticationService.java | 44 +++ .../blogapi/service/JwtService.java | 84 +++++ .../impl/CustomUserDetailsServiceImpl.java | 32 +- src/main/resources/application.yml | 13 +- 47 files changed, 1654 insertions(+), 1573 deletions(-) create mode 100644 src/main/java/com/sopromadze/blogapi/config/ApplicationConfiguration.java create mode 100644 src/main/java/com/sopromadze/blogapi/config/JwtAuthenticationFilter.java create mode 100644 src/main/java/com/sopromadze/blogapi/config/SecurityConfig.java delete mode 100644 src/main/java/com/sopromadze/blogapi/config/SecutiryConfig.java delete mode 100644 src/main/java/com/sopromadze/blogapi/controller/AuthController.java create mode 100644 src/main/java/com/sopromadze/blogapi/controller/AuthenticationController.java create mode 100644 src/main/java/com/sopromadze/blogapi/payload/LoginResponse.java create mode 100644 src/main/java/com/sopromadze/blogapi/payload/LoginUserDto.java create mode 100644 src/main/java/com/sopromadze/blogapi/payload/RegisterUserDto.java delete mode 100644 src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationEntryPoint.java delete mode 100644 src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationFilter.java delete mode 100644 src/main/java/com/sopromadze/blogapi/security/JwtTokenProvider.java create mode 100644 src/main/java/com/sopromadze/blogapi/service/AuthenticationService.java create mode 100644 src/main/java/com/sopromadze/blogapi/service/JwtService.java diff --git a/pom.xml b/pom.xml index 0b7dd2c5..ebc8cd1e 100644 --- a/pom.xml +++ b/pom.xml @@ -15,14 +15,14 @@ org.springframework.boot spring-boot-starter-parent - 2.1.8.RELEASE - + 3.2.5 + UTF-8 UTF-8 - 1.8 + 21 coma123 https://sonarcloud.io @@ -39,13 +39,6 @@ runtime - - org.projectlombok - lombok - 1.18.30 - provided - - org.springframework.boot spring-boot-starter-web @@ -56,14 +49,26 @@ spring-boot-starter-aop - org.springframework.boot spring-boot-starter-test test - + + jakarta.validation + jakarta.validation-api + 3.0.2 + + + + org.projectlombok + lombok + 1.18.32 + provided + + + org.mockito mockito-all @@ -122,13 +127,18 @@ io.jsonwebtoken - jjwt - 0.9.1 + jjwt-api + 0.11.5 - - javax.xml.bind - jaxb-api + io.jsonwebtoken + jjwt-impl + 0.11.5 + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 diff --git a/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java b/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java index 6e45ce6b..0329c2c7 100644 --- a/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java +++ b/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java @@ -1,6 +1,6 @@ package com.sopromadze.blogapi; -import com.sopromadze.blogapi.security.JwtAuthenticationFilter; +import jakarta.annotation.PostConstruct; import org.modelmapper.ModelMapper; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -8,31 +8,25 @@ import org.springframework.context.annotation.Bean; import org.springframework.data.convert.Jsr310Converters; -import javax.annotation.PostConstruct; 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 ModelMapper modelMapper() { - return new ModelMapper(); - } + @Bean + public ModelMapper modelMapper() { + return new ModelMapper(); + } } diff --git a/src/main/java/com/sopromadze/blogapi/config/ApplicationConfiguration.java b/src/main/java/com/sopromadze/blogapi/config/ApplicationConfiguration.java new file mode 100644 index 00000000..074aef8c --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/config/ApplicationConfiguration.java @@ -0,0 +1,45 @@ +package com.sopromadze.blogapi.config; + +import com.sopromadze.blogapi.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +@Configuration +@RequiredArgsConstructor +public class ApplicationConfiguration { + private final UserRepository userRepository; + + @Bean + public UserDetailsService userDetailsService() { + return username -> userRepository.findByEmail(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found")); + } + + @Bean + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { + return config.getAuthenticationManager(); + } + + @Bean + AuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + + authProvider.setUserDetailsService(userDetailsService()); + authProvider.setPasswordEncoder(passwordEncoder()); + + return authProvider; + } +} diff --git a/src/main/java/com/sopromadze/blogapi/config/JwtAuthenticationFilter.java b/src/main/java/com/sopromadze/blogapi/config/JwtAuthenticationFilter.java new file mode 100644 index 00000000..894fb802 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/config/JwtAuthenticationFilter.java @@ -0,0 +1,69 @@ +package com.sopromadze.blogapi.config; + +import com.sopromadze.blogapi.service.JwtService; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.lang.NonNull; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.HandlerExceptionResolver; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final HandlerExceptionResolver handlerExceptionResolver; + private final JwtService jwtService; + private final UserDetailsService userDetailsService; + + @Override + protected void doFilterInternal( + @NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain + ) throws ServletException, IOException { + final String authHeader = request.getHeader("Authorization"); + + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + } + + try { + final String jwt = authHeader.substring(7); + final String userEmail = jwtService.extractUsername(jwt); + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (userEmail != null && authentication == null) { + UserDetails userDetails = userDetailsService.loadUserByUsername(userEmail); + + if (jwtService.isTokenValid(jwt, userDetails)) { + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( + userDetails, + null, + userDetails.getAuthorities() + ); + + authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authToken); + } + } + + filterChain.doFilter(request, response); + } catch (Exception exception) { + handlerExceptionResolver.resolveException(request, response, null, exception); + } + } +} diff --git a/src/main/java/com/sopromadze/blogapi/config/SecurityConfig.java b/src/main/java/com/sopromadze/blogapi/config/SecurityConfig.java new file mode 100644 index 00000000..e426ba98 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/config/SecurityConfig.java @@ -0,0 +1,59 @@ +package com.sopromadze.blogapi.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.List; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + private final AuthenticationProvider authenticationProvider; + private final JwtAuthenticationFilter jwtAuthenticationFilter; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests() + .requestMatchers(HttpMethod.GET, "/api/**").permitAll() + .requestMatchers(HttpMethod.POST, "/api/auth/**").permitAll() + .requestMatchers(HttpMethod.GET, "/api/users/checkUsernameAvailability", "/api/users/checkEmailAvailability").permitAll() + .anyRequest() + .authenticated() + .and() + .sessionManagement( + config -> config.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + .authenticationProvider(authenticationProvider) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + + configuration.setAllowedOrigins(List.of("http://localhost:8080")); + configuration.setAllowedMethods(List.of("GET", "POST")); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + + return source; + } +} diff --git a/src/main/java/com/sopromadze/blogapi/config/SecutiryConfig.java b/src/main/java/com/sopromadze/blogapi/config/SecutiryConfig.java deleted file mode 100644 index 1a0c1175..00000000 --- a/src/main/java/com/sopromadze/blogapi/config/SecutiryConfig.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.sopromadze.blogapi.config; - -import com.sopromadze.blogapi.repository.UserRepository; -import com.sopromadze.blogapi.security.JwtAuthenticationEntryPoint; -import com.sopromadze.blogapi.security.JwtAuthenticationFilter; -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; -import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.BeanIds; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -@Configuration -@EnableWebSecurity -@EnableGlobalMethodSecurity( - securedEnabled = true, - jsr250Enabled = true, - prePostEnabled = true) -public class SecutiryConfig extends WebSecurityConfigurerAdapter { - private final CustomUserDetailsServiceImpl customUserDetailsService; - private final JwtAuthenticationEntryPoint unauthorizedHandler; - private final 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(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()); - } - - @Bean(BeanIds.AUTHENTICATION_MANAGER) - public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } -} diff --git a/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java b/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java index b5dc9ff4..46edab2e 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java @@ -28,63 +28,63 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; +import jakarta.validation.Valid; @RestController @RequestMapping("/api/albums") public class AlbumController { - @Autowired - private AlbumService albumService; + @Autowired + private AlbumService albumService; - @Autowired - private PhotoService photoService; + @Autowired + private PhotoService photoService; - @ExceptionHandler(ResponseEntityErrorException.class) - public ResponseEntity handleExceptions(ResponseEntityErrorException exception) { - return exception.getApiResponse(); - } + @ExceptionHandler(ResponseEntityErrorException.class) + public ResponseEntity handleExceptions(ResponseEntityErrorException exception) { + return exception.getApiResponse(); + } - @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 + 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); - return albumService.getAllAlbums(page, size); - } + return albumService.getAllAlbums(page, size); + } - @PostMapping - @PreAuthorize("hasRole('USER')") - public ResponseEntity addAlbum(@Valid @RequestBody AlbumRequest albumRequest, @CurrentUser UserPrincipal currentUser) { - return albumService.addAlbum(albumRequest, currentUser); - } + @PostMapping + @PreAuthorize("hasRole('USER')") + public ResponseEntity addAlbum(@Valid @RequestBody AlbumRequest albumRequest, @CurrentUser UserPrincipal currentUser) { + return albumService.addAlbum(albumRequest, currentUser); + } - @GetMapping("/{id}") - public ResponseEntity getAlbum(@PathVariable(name = "id") Long id) { - return albumService.getAlbum(id); - } + @GetMapping("/{id}") + public ResponseEntity getAlbum(@PathVariable(name = "id") Long id) { + return albumService.getAlbum(id); + } - @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); - } + @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); - } + @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) { + @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); + PagedResponse response = photoService.getAllPhotosByAlbum(id, page, size); - return new ResponseEntity<>(response, HttpStatus.OK); - } + 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 deleted file mode 100644 index 5d49753d..00000000 --- a/src/main/java/com/sopromadze/blogapi/controller/AuthController.java +++ /dev/null @@ -1,108 +0,0 @@ -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; -import com.sopromadze.blogapi.payload.ApiResponse; -import com.sopromadze.blogapi.payload.JwtAuthenticationResponse; -import com.sopromadze.blogapi.payload.LoginRequest; -import com.sopromadze.blogapi.payload.SignUpRequest; -import com.sopromadze.blogapi.repository.RoleRepository; -import com.sopromadze.blogapi.repository.UserRepository; -import com.sopromadze.blogapi.security.JwtTokenProvider; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; - -import javax.validation.Valid; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -@RestController -@RequestMapping("/api/auth") -public class AuthController { - private static final String USER_ROLE_NOT_SET = "User role not set"; - - @Autowired - private AuthenticationManager authenticationManager; - - @Autowired - private UserRepository userRepository; - - @Autowired - private RoleRepository roleRepository; - - @Autowired - private PasswordEncoder passwordEncoder; - - @Autowired - private JwtTokenProvider jwtTokenProvider; - - @PostMapping("/signin") - public ResponseEntity authenticateUser(@Valid @RequestBody LoginRequest loginRequest) { - Authentication authentication = authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(loginRequest.getUsernameOrEmail(), loginRequest.getPassword())); - - SecurityContextHolder.getContext().setAuthentication(authentication); - - String jwt = jwtTokenProvider.generateToken(authentication); - return ResponseEntity.ok(new JwtAuthenticationResponse(jwt)); - } - - @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 (Boolean.TRUE.equals(userRepository.existsByEmail(signUpRequest.getEmail()))) { - throw new BlogapiException(HttpStatus.BAD_REQUEST, "Email is already taken"); - } - - String firstName = signUpRequest.getFirstName().toLowerCase(); - - String lastName = signUpRequest.getLastName().toLowerCase(); - - String username = signUpRequest.getUsername().toLowerCase(); - - String email = signUpRequest.getEmail().toLowerCase(); - - String password = passwordEncoder.encode(signUpRequest.getPassword()); - - User user = new User(firstName, lastName, username, email, password); - - 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.setRoles(roles); - - 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/AuthenticationController.java b/src/main/java/com/sopromadze/blogapi/controller/AuthenticationController.java new file mode 100644 index 00000000..e1eef70a --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/controller/AuthenticationController.java @@ -0,0 +1,47 @@ +package com.sopromadze.blogapi.controller; + +import com.sopromadze.blogapi.model.user.User; +import com.sopromadze.blogapi.payload.LoginResponse; +import com.sopromadze.blogapi.payload.LoginUserDto; +import com.sopromadze.blogapi.payload.RegisterUserDto; +import com.sopromadze.blogapi.service.AuthenticationService; +import com.sopromadze.blogapi.service.JwtService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/auth") +@RestController +public class AuthenticationController { + private final JwtService jwtService; + + private final AuthenticationService authenticationService; + + public AuthenticationController(JwtService jwtService, AuthenticationService authenticationService) { + this.jwtService = jwtService; + this.authenticationService = authenticationService; + } + + @PostMapping("/signup") + public ResponseEntity register(@RequestBody RegisterUserDto registerUserDto) { + User registeredUser = authenticationService.signup(registerUserDto); + + return ResponseEntity.ok(registeredUser); + } + + @PostMapping("/login") + public ResponseEntity authenticate(@RequestBody LoginUserDto loginUserDto) { + User authenticatedUser = authenticationService.authenticate(loginUserDto); + + String jwtToken = jwtService.generateToken(authenticatedUser); + + LoginResponse loginResponse = LoginResponse.builder() + .token(jwtToken) + .expiresIn(jwtService.getExpirationTime()) + .build(); + + return ResponseEntity.ok(loginResponse); + } +} diff --git a/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java b/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java index 39b63842..c56741ad 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java @@ -21,46 +21,46 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; +import jakarta.validation.Valid; @RestController @RequestMapping("/api/categories") public class CategoryController { - @Autowired - private CategoryService categoryService; + @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); - } + @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) { + @PostMapping + @PreAuthorize("hasRole('USER')") + public ResponseEntity addCategory(@Valid @RequestBody Category category, + @CurrentUser UserPrincipal currentUser) { - return categoryService.addCategory(category, currentUser); - } + return categoryService.addCategory(category, currentUser); + } - @GetMapping("/{id}") - public ResponseEntity getCategory(@PathVariable(name = "id") Long id) { - return categoryService.getCategory(id); - } + @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); - } + @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); - } + @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 2a402910..b4fc98f9 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/CommentController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/CommentController.java @@ -22,62 +22,62 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; +import jakarta.validation.Valid; @RestController @RequestMapping("/api/posts/{postId}/comments") public class CommentController { - @Autowired - private CommentService commentService; + @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) { + @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); + PagedResponse allComments = commentService.getAllComments(postId, page, size); - return new ResponseEntity< >(allComments, HttpStatus.OK); - } + 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); + @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); - } + 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); + @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); - } + 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) { + @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); + Comment updatedComment = commentService.updateComment(postId, id, commentRequest, currentUser); - return new ResponseEntity<>(updatedComment, HttpStatus.OK); - } + 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) { + @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); + ApiResponse response = commentService.deleteComment(postId, id, currentUser); - HttpStatus status = response.getSuccess() ? HttpStatus.OK : HttpStatus.BAD_REQUEST; + HttpStatus status = response.getSuccess() ? HttpStatus.OK : HttpStatus.BAD_REQUEST; - return new ResponseEntity<>(response, status); - } + 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 2f0846d0..d6f8d99f 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java @@ -22,52 +22,52 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; +import jakarta.validation.Valid; @RestController @RequestMapping("/api/photos") public class PhotoController { - @Autowired - private PhotoService photoService; + @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); - } + @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); + @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); - } + return new ResponseEntity<>(photoResponse, HttpStatus.OK); + } - @GetMapping("/{id}") - public ResponseEntity getPhoto(@PathVariable(name = "id") Long id) { - PhotoResponse photoResponse = photoService.getPhoto(id); + @GetMapping("/{id}") + public ResponseEntity getPhoto(@PathVariable(name = "id") Long id) { + PhotoResponse photoResponse = photoService.getPhoto(id); - return new ResponseEntity< >(photoResponse, HttpStatus.OK); - } + 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) { + @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); + PhotoResponse photoResponse = photoService.updatePhoto(id, photoRequest, currentUser); - return new ResponseEntity< >(photoResponse, HttpStatus.OK); - } + 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); + @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); - } + 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 5b8efcc7..9f5c15cf 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/PostController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/PostController.java @@ -23,73 +23,73 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; +import jakarta.validation.Valid; @RestController @RequestMapping("/api/posts") public class PostController { - @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); - } + @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 index 9a9b45c8..3481f9e9 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/TagController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/TagController.java @@ -21,54 +21,54 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; +import jakarta.validation.Valid; @RestController @RequestMapping("/api/tags") public class TagController { - @Autowired - private TagService tagService; + @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) { + @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); + PagedResponse response = tagService.getAllTags(page, size); - return new ResponseEntity< >(response, HttpStatus.OK); - } + 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); + @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); - } + return new ResponseEntity<>(newTag, HttpStatus.CREATED); + } - @GetMapping("/{id}") - public ResponseEntity getTag(@PathVariable(name = "id") Long id) { - Tag tag = tagService.getTag(id); + @GetMapping("/{id}") + public ResponseEntity getTag(@PathVariable(name = "id") Long id) { + Tag tag = tagService.getTag(id); - return new ResponseEntity< >(tag, HttpStatus.OK); - } + 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) { + @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); + Tag updatedTag = tagService.updateTag(id, tag, currentUser); - return new ResponseEntity< >(updatedTag, HttpStatus.OK); - } + 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); + @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); - } + 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 aeb57789..6f9fd512 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/TodoController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/TodoController.java @@ -21,75 +21,75 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; +import jakarta.validation.Valid; @RestController @RequestMapping("/api/todos") public class TodoController { - @Autowired - private TodoService todoService; + @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) { + @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); + PagedResponse response = todoService.getAllTodos(currentUser, page, size); - return new ResponseEntity< >(response, HttpStatus.OK); - } + 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); + @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); - } + 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); + @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); - } + 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); + @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); - } + 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); + @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); - } + return new ResponseEntity<>(apiResponse, HttpStatus.OK); + } - @PutMapping("/{id}/complete") - @PreAuthorize("hasRole('USER')") - public ResponseEntity completeTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) { + @PutMapping("/{id}/complete") + @PreAuthorize("hasRole('USER')") + public ResponseEntity completeTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) { - Todo todo = todoService.completeTodo(id, currentUser); + Todo todo = todoService.completeTodo(id, currentUser); - return new ResponseEntity< >(todo, HttpStatus.OK); - } + return new ResponseEntity<>(todo, HttpStatus.OK); + } - @PutMapping("/{id}/unComplete") - @PreAuthorize("hasRole('USER')") - public ResponseEntity unCompleteTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) { + @PutMapping("/{id}/unComplete") + @PreAuthorize("hasRole('USER')") + public ResponseEntity unCompleteTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) { - Todo todo = todoService.unCompleteTodo(id, currentUser); + Todo todo = todoService.unCompleteTodo(id, currentUser); - return new ResponseEntity< >(todo, HttpStatus.OK); - } + 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 f6b717e2..65bfdc68 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/UserController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/UserController.java @@ -29,116 +29,116 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; +import jakarta.validation.Valid; @RestController @RequestMapping("/api/users") public class UserController { - @Autowired - private UserService userService; + @Autowired + private UserService userService; - @Autowired - private PostService postService; + @Autowired + private PostService postService; - @Autowired - private AlbumService albumService; + @Autowired + private AlbumService albumService; - @GetMapping("/me") - @PreAuthorize("hasRole('USER')") - public ResponseEntity getCurrentUser(@CurrentUser UserPrincipal currentUser) { - UserSummary userSummary = userService.getCurrentUser(currentUser); + @GetMapping("/me") + @PreAuthorize("hasRole('USER')") + public ResponseEntity getCurrentUser(@CurrentUser UserPrincipal currentUser) { + UserSummary userSummary = userService.getCurrentUser(currentUser); - return new ResponseEntity< >(userSummary, HttpStatus.OK); - } + return new ResponseEntity<>(userSummary, HttpStatus.OK); + } - @GetMapping("/checkUsernameAvailability") - public ResponseEntity checkUsernameAvailability(@RequestParam(value = "username") String username) { - UserIdentityAvailability userIdentityAvailability = userService.checkUsernameAvailability(username); + @GetMapping("/checkUsernameAvailability") + public ResponseEntity checkUsernameAvailability(@RequestParam(value = "username") String username) { + UserIdentityAvailability userIdentityAvailability = userService.checkUsernameAvailability(username); - return new ResponseEntity< >(userIdentityAvailability, HttpStatus.OK); - } + 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("/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); + @GetMapping("/{username}/profile") + public ResponseEntity getUSerProfile(@PathVariable(value = "username") String username) { + UserProfile userProfile = userService.getUserProfile(username); - return new ResponseEntity< >(userProfile, HttpStatus.OK); - } + 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); + @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); - } + 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) { + @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); + PagedResponse response = albumService.getUserAlbums(username, page, size); - return new ResponseEntity< >(response, HttpStatus.OK); - } + return new ResponseEntity<>(response, HttpStatus.OK); + } - @PostMapping - @PreAuthorize("hasRole('ADMIN')") - public ResponseEntity addUser(@Valid @RequestBody User user) { - User newUser = userService.addUser(user); + @PostMapping + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity addUser(@Valid @RequestBody User user) { + User newUser = userService.addUser(user); - return new ResponseEntity< >(newUser, HttpStatus.CREATED); - } + 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); + @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); - } + 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); + @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); - } + 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); + @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); - } + 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); + @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); - } + 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); + @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); - } + return new ResponseEntity<>(userProfile, HttpStatus.OK); + } } diff --git a/src/main/java/com/sopromadze/blogapi/model/Album.java b/src/main/java/com/sopromadze/blogapi/model/Album.java index 311d5991..dde10393 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Album.java +++ b/src/main/java/com/sopromadze/blogapi/model/Album.java @@ -6,19 +6,20 @@ 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 jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import jakarta.validation.constraints.NotBlank; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -26,39 +27,39 @@ @EqualsAndHashCode(callSuper = true) @Entity @Data -@Table(name = "albums", uniqueConstraints = { @UniqueConstraint(columnNames = { "title" }) }) +@Table(name = "albums", uniqueConstraints = {@UniqueConstraint(columnNames = {"title"})}) public class Album extends UserDateAudit { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - @NotBlank - @Column(name = "title") - private String title; + @NotBlank + @Column(name = "title") + private String title; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - private User user; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; - @OneToMany(mappedBy = "album", cascade = CascadeType.ALL, orphanRemoval = true) - private List photo; + @OneToMany(mappedBy = "album", cascade = CascadeType.ALL, orphanRemoval = true) + private List photo; - @JsonIgnore - public User getUser() { - return user; - } + @JsonIgnore + public User getUser() { + return user; + } - public List getPhoto() { - return this.photo == null ? null : new ArrayList<>(this.photo); - } + 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); - } - } + 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 index 102c6e39..78987447 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Category.java +++ b/src/main/java/com/sopromadze/blogapi/model/Category.java @@ -7,14 +7,15 @@ 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 jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -26,33 +27,33 @@ @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); - } - } + 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.java b/src/main/java/com/sopromadze/blogapi/model/Comment.java index d9d21972..5f896a79 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Comment.java +++ b/src/main/java/com/sopromadze/blogapi/model/Comment.java @@ -7,18 +7,18 @@ 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; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; @EqualsAndHashCode(callSuper = true) @Entity diff --git a/src/main/java/com/sopromadze/blogapi/model/Photo.java b/src/main/java/com/sopromadze/blogapi/model/Photo.java index 7aaa1d2a..9f03fb6a 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Photo.java +++ b/src/main/java/com/sopromadze/blogapi/model/Photo.java @@ -6,55 +6,55 @@ 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; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import jakarta.validation.constraints.NotBlank; @EqualsAndHashCode(callSuper = true) @Entity @Data @NoArgsConstructor -@Table(name = "photos", uniqueConstraints = { @UniqueConstraint(columnNames = { "title" }) }) +@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; - } + 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 index 6b97dbcf..3c0e945e 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Post.java +++ b/src/main/java/com/sopromadze/blogapi/model/Post.java @@ -8,20 +8,21 @@ 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 jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -29,67 +30,67 @@ @EqualsAndHashCode(callSuper = true) @Entity @Data -@Table(name = "posts", uniqueConstraints = { @UniqueConstraint(columnNames = { "title" }) }) +@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); - } - } + 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 index a3cbd193..d9ba7431 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Tag.java +++ b/src/main/java/com/sopromadze/blogapi/model/Tag.java @@ -2,21 +2,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.sopromadze.blogapi.model.audit.UserDateAudit; -import com.sopromadze.blogapi.model.Post; +import jakarta.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.JoinTable; -import javax.persistence.ManyToMany; -import javax.persistence.Table; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -29,35 +19,35 @@ //@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Tag extends UserDateAudit { - private static final long serialVersionUID = -5298707266367331514L; + private static final long serialVersionUID = -5298707266367331514L; - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - @Column(name = "name") - private String name; + @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; + @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 Tag(String name) { + super(); + this.name = name; + } - public List getPosts() { - return posts == null ? null : new ArrayList<>(posts); - } + 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 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 index 7fed93d1..555a0bea 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Todo.java +++ b/src/main/java/com/sopromadze/blogapi/model/Todo.java @@ -6,43 +6,43 @@ 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; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import jakarta.validation.constraints.NotBlank; @EqualsAndHashCode(callSuper = true) @Entity @Data -@Table(name = "todos", uniqueConstraints = { @UniqueConstraint(columnNames = { "title" }) }) +@Table(name = "todos", uniqueConstraints = {@UniqueConstraint(columnNames = {"title"})}) public class Todo extends UserDateAudit { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - @NotBlank - @Column(name = "title") - private String title; + @NotBlank + @Column(name = "title") + private String title; - @Column(name = "completed") - private Boolean completed; + @Column(name = "completed") + private Boolean completed; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - private User user; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; - @JsonIgnore - public User getUser() { - return user; - } + @JsonIgnore + public User getUser() { + return 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 c9f8abe7..259bdf5d 100644 --- a/src/main/java/com/sopromadze/blogapi/model/audit/DateAudit.java +++ b/src/main/java/com/sopromadze/blogapi/model/audit/DateAudit.java @@ -6,9 +6,10 @@ import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import javax.persistence.Column; -import javax.persistence.EntityListeners; -import javax.persistence.MappedSuperclass; +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; + import java.io.Serializable; import java.time.Instant; @@ -16,19 +17,19 @@ @Data @EntityListeners(AuditingEntityListener.class) @JsonIgnoreProperties( - value = { "createdAt", "updatedAt" }, - allowGetters = true + value = {"createdAt", "updatedAt"}, + allowGetters = true ) public abstract class DateAudit implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - @CreatedDate - @Column(nullable = false, updatable = false) - private Instant createdAt; + @CreatedDate + @Column(nullable = false, updatable = false) + private Instant createdAt; - @LastModifiedDate - @Column(nullable = false) - private Instant updatedAt; + @LastModifiedDate + @Column(nullable = false) + private Instant 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 10644bd5..14e4446b 100644 --- a/src/main/java/com/sopromadze/blogapi/model/audit/UserDateAudit.java +++ b/src/main/java/com/sopromadze/blogapi/model/audit/UserDateAudit.java @@ -6,23 +6,23 @@ import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.LastModifiedBy; -import javax.persistence.Column; -import javax.persistence.MappedSuperclass; +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; @EqualsAndHashCode(callSuper = true) @MappedSuperclass @Data @JsonIgnoreProperties( - value = { "createdBY", "updatedBy" }, - allowGetters = true + value = {"createdBY", "updatedBy"}, + allowGetters = true ) public abstract class UserDateAudit extends DateAudit { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - @CreatedBy - @Column(updatable = false) - private Long createdBy; + @CreatedBy + @Column(updatable = false) + private Long createdBy; - @LastModifiedBy - private Long updatedBy; + @LastModifiedBy + private Long updatedBy; } 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 00231ca9..9f45a401 100644 --- a/src/main/java/com/sopromadze/blogapi/model/role/Role.java +++ b/src/main/java/com/sopromadze/blogapi/model/role/Role.java @@ -4,30 +4,30 @@ import lombok.NoArgsConstructor; import org.hibernate.annotations.NaturalId; -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; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; @Entity @Data @NoArgsConstructor @Table(name = "roles") public class Role { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - @Enumerated(EnumType.STRING) - @NaturalId - @Column(name = "name") - private RoleName name; + @Enumerated(EnumType.STRING) + @NaturalId + @Column(name = "name") + private RoleName name; - public Role(RoleName name) { - this.name = name; - } + public Role(RoleName name) { + this.name = name; + } } 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 f686bbbf..ef028ca9 100644 --- a/src/main/java/com/sopromadze/blogapi/model/user/Address.java +++ b/src/main/java/com/sopromadze/blogapi/model/user/Address.java @@ -2,20 +2,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.sopromadze.blogapi.model.audit.UserDateAudit; -import lombok.AllArgsConstructor; +import jakarta.persistence.*; 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) @@ -24,89 +15,89 @@ @NoArgsConstructor @Table(name = "address") public class Address extends UserDateAudit { - 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); - } + 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 7cc93a2f..df93e8cd 100644 --- a/src/main/java/com/sopromadze/blogapi/model/user/Company.java +++ b/src/main/java/com/sopromadze/blogapi/model/user/Company.java @@ -6,13 +6,14 @@ 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 jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; + import java.time.Instant; @EqualsAndHashCode(callSuper = true) @@ -21,85 +22,85 @@ @NoArgsConstructor @Table(name = "company") public class Company extends UserDateAudit { - 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); - } + 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 f2845e22..c7bbfe2f 100644 --- a/src/main/java/com/sopromadze/blogapi/model/user/Geo.java +++ b/src/main/java/com/sopromadze/blogapi/model/user/Geo.java @@ -6,13 +6,14 @@ 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 jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; + import java.time.Instant; @EqualsAndHashCode(callSuper = true) @@ -21,72 +22,72 @@ @NoArgsConstructor @Table(name = "geo") public class Geo extends UserDateAudit { - 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); - } + 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 90f2e489..9bf7efd9 100644 --- a/src/main/java/com/sopromadze/blogapi/model/user/User.java +++ b/src/main/java/com/sopromadze/blogapi/model/user/User.java @@ -3,34 +3,23 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.sopromadze.blogapi.model.Album; -import com.sopromadze.blogapi.model.audit.DateAudit; 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; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; +import com.sopromadze.blogapi.model.audit.DateAudit; +import com.sopromadze.blogapi.model.role.Role; +import jakarta.persistence.*; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.*; import org.hibernate.annotations.NaturalId; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; -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.io.Serial; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -38,155 +27,182 @@ @Entity @Data @NoArgsConstructor -@Table(name = "users", uniqueConstraints = { @UniqueConstraint(columnNames = { "username" }), - @UniqueConstraint(columnNames = { "email" }) }) -public class User extends DateAudit { - private static final long serialVersionUID = 1L; - - @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; +@AllArgsConstructor +@Builder +@Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = {"username"}), + @UniqueConstraint(columnNames = {"email"})}) +public class User extends DateAudit implements UserDetails { + @Serial + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @NotBlank + @Column(name = "first_name") + @Size(max = 40) + private String firstName; - @JsonIgnore - @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) - private List comments; + @NotBlank + @Column(name = "last_name") + @Size(max = 40) + private String lastName; - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) - @JoinColumn(name = "company_id") - private Company company; + @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; - 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; - } + @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; - public List getTodos() { + @Column(name = "website") + private String website; - return todos == null ? null : new ArrayList<>(todos); - } + @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 setTodos(List todos) { + @JsonIgnore + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + private List todos; - if (todos == null) { - this.todos = null; - } else { - this.todos = Collections.unmodifiableList(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(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 getAlbums() { - return albums == null ? null : new ArrayList<>(albums); - } + public List getPosts() { + + return posts == null ? null : new ArrayList<>(posts); + } - public void setAlbums(List albums) { + public void setPosts(List posts) { - if (albums == null) { - this.albums = null; - } else { - this.albums = Collections.unmodifiableList(albums); - } - } + if (posts == null) { + this.posts = null; + } else { + this.posts = Collections.unmodifiableList(posts); + } + } + public List getRoles() { - public List getPosts() { + return roles == null ? null : new ArrayList<>(roles); + } - return posts == null ? null : new ArrayList<>(posts); - } + public void setRoles(List roles) { - public void setPosts(List posts) { + if (roles == null) { + this.roles = null; + } else { + this.roles = Collections.unmodifiableList(roles); + } + } - if (posts == null) { - this.posts = null; - } else { - this.posts = Collections.unmodifiableList(posts); - } - } + public List getComments() { + return comments == null ? null : new ArrayList<>(comments); + } - public List getRoles() { + public void setComments(List comments) { - return roles == null ? null : new ArrayList<>(roles); - } + if (comments == null) { + this.comments = null; + } else { + this.comments = Collections.unmodifiableList(comments); + } + } - public void setRoles(List roles) { + @Override + public Collection getAuthorities() { + return List.of(); + } - if (roles == null) { - this.roles = null; - } else { - this.roles = Collections.unmodifiableList(roles); - } - } + @Override + public boolean isAccountNonExpired() { + return true; + } - public List getComments() { - return comments == null ? null : new ArrayList<>(comments); - } + @Override + public boolean isAccountNonLocked() { + return true; + } - public void setComments(List comments) { + @Override + public boolean isCredentialsNonExpired() { + return true; + } - if (comments == null) { - this.comments = null; - } else { - this.comments = Collections.unmodifiableList(comments); - } - } + @Override + public boolean isEnabled() { + return true; + } } diff --git a/src/main/java/com/sopromadze/blogapi/payload/CommentRequest.java b/src/main/java/com/sopromadze/blogapi/payload/CommentRequest.java index c88239ff..0854597e 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/CommentRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/CommentRequest.java @@ -2,12 +2,12 @@ import lombok.Data; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; @Data public class CommentRequest { - @NotBlank - @Size(min = 10, message = "Comment body must be minimum 10 characters") - private String 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/InfoRequest.java b/src/main/java/com/sopromadze/blogapi/payload/InfoRequest.java index 7baf210f..90df3bcf 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/InfoRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/InfoRequest.java @@ -2,34 +2,34 @@ import lombok.Data; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; @Data public class InfoRequest { - @NotBlank - private String street; + @NotBlank + private String street; - @NotBlank - private String suite; + @NotBlank + private String suite; - @NotBlank - private String city; + @NotBlank + private String city; - @NotBlank - private String zipcode; + @NotBlank + private String zipcode; - private String companyName; + private String companyName; - private String catchPhrase; + private String catchPhrase; - private String bs; + private String bs; - private String website; + private String website; - private String phone; + private String phone; - private String lat; + private String lat; - private String lng; + private String lng; } diff --git a/src/main/java/com/sopromadze/blogapi/payload/LoginRequest.java b/src/main/java/com/sopromadze/blogapi/payload/LoginRequest.java index ac1b0ee8..617b0572 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/LoginRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/LoginRequest.java @@ -2,13 +2,13 @@ import lombok.Data; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; @Data public class LoginRequest { - @NotBlank - private String usernameOrEmail; + @NotBlank + private String usernameOrEmail; - @NotBlank - private String password; + @NotBlank + private String password; } diff --git a/src/main/java/com/sopromadze/blogapi/payload/LoginResponse.java b/src/main/java/com/sopromadze/blogapi/payload/LoginResponse.java new file mode 100644 index 00000000..9b4027f9 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/payload/LoginResponse.java @@ -0,0 +1,16 @@ +package com.sopromadze.blogapi.payload; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class LoginResponse { + private String token; + + private long expiresIn; +} diff --git a/src/main/java/com/sopromadze/blogapi/payload/LoginUserDto.java b/src/main/java/com/sopromadze/blogapi/payload/LoginUserDto.java new file mode 100644 index 00000000..ca62b79b --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/payload/LoginUserDto.java @@ -0,0 +1,15 @@ +package com.sopromadze.blogapi.payload; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class LoginUserDto { + private String email; + private String password; +} diff --git a/src/main/java/com/sopromadze/blogapi/payload/PhotoRequest.java b/src/main/java/com/sopromadze/blogapi/payload/PhotoRequest.java index 74950489..dbfda2ba 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/PhotoRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/PhotoRequest.java @@ -2,25 +2,25 @@ import lombok.Data; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; @Data public class PhotoRequest { - @NotBlank - @Size(min = 3) - private String title; + @NotBlank + @Size(min = 3) + private String title; - @NotBlank - @Size(min = 10) - private String url; + @NotBlank + @Size(min = 10) + private String url; - @NotBlank - @Size(min = 10) - private String thumbnailUrl; + @NotBlank + @Size(min = 10) + private String thumbnailUrl; - @NotNull - private Long albumId; + @NotNull + private Long albumId; } diff --git a/src/main/java/com/sopromadze/blogapi/payload/PostRequest.java b/src/main/java/com/sopromadze/blogapi/payload/PostRequest.java index 28d7ed13..86742bf3 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/PostRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/PostRequest.java @@ -2,9 +2,10 @@ import lombok.Data; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -12,30 +13,30 @@ @Data public class PostRequest { - @NotBlank - @Size(min = 10) - private String title; + @NotBlank + @Size(min = 10) + private String title; - @NotBlank - @Size(min = 50) - private String body; + @NotBlank + @Size(min = 50) + private String body; - @NotNull - private Long categoryId; + @NotNull + private Long categoryId; - private List tags; + private List tags; - public List getTags() { + public List getTags() { - return tags == null ? Collections.emptyList() : new ArrayList<>(tags); - } + return tags == null ? Collections.emptyList() : new ArrayList<>(tags); + } - public void setTags(List tags) { + public void setTags(List tags) { - if (tags == null) { - this.tags = null; - } else { - this.tags = Collections.unmodifiableList(tags); - } - } + if (tags == null) { + this.tags = null; + } else { + this.tags = Collections.unmodifiableList(tags); + } + } } diff --git a/src/main/java/com/sopromadze/blogapi/payload/RegisterUserDto.java b/src/main/java/com/sopromadze/blogapi/payload/RegisterUserDto.java new file mode 100644 index 00000000..4b31726c --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/payload/RegisterUserDto.java @@ -0,0 +1,17 @@ +package com.sopromadze.blogapi.payload; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class RegisterUserDto { + private String email; + private String password; + private String firstName; + private String lastName; +} diff --git a/src/main/java/com/sopromadze/blogapi/payload/SignUpRequest.java b/src/main/java/com/sopromadze/blogapi/payload/SignUpRequest.java index 3f993f13..2fd4b2fa 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/SignUpRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/SignUpRequest.java @@ -2,30 +2,30 @@ import lombok.Data; -import javax.validation.constraints.Email; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; @Data public class SignUpRequest { - @NotBlank - @Size(min = 4, max = 40) - private String firstName; + @NotBlank + @Size(min = 4, max = 40) + private String firstName; - @NotBlank - @Size(min = 4, max = 40) - private String lastName; + @NotBlank + @Size(min = 4, max = 40) + private String lastName; - @NotBlank - @Size(min = 3, max = 15) - private String username; + @NotBlank + @Size(min = 3, max = 15) + private String username; - @NotBlank - @Size(max = 40) - @Email - private String email; + @NotBlank + @Size(max = 40) + @Email + private String email; - @NotBlank - @Size(min = 6, max = 20) - private String password; + @NotBlank + @Size(min = 6, max = 20) + private String password; } diff --git a/src/main/java/com/sopromadze/blogapi/repository/UserRepository.java b/src/main/java/com/sopromadze/blogapi/repository/UserRepository.java index 94290248..9fd36ea2 100644 --- a/src/main/java/com/sopromadze/blogapi/repository/UserRepository.java +++ b/src/main/java/com/sopromadze/blogapi/repository/UserRepository.java @@ -6,27 +6,28 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; + import java.util.Optional; @Repository public interface UserRepository extends JpaRepository { - Optional findByUsername(@NotBlank String username); + Optional findByUsername(@NotBlank String username); - Optional findByEmail(@NotBlank String email); + Optional findByEmail(@NotBlank String email); - Boolean existsByUsername(@NotBlank String username); + Boolean existsByUsername(@NotBlank String username); - Boolean existsByEmail(@NotBlank String email); + Boolean existsByEmail(@NotBlank String email); - Optional findByUsernameOrEmail(String username, String email); + Optional findByUsernameOrEmail(String username, String email); - default User getUser(UserPrincipal currentUser) { - return getUserByName(currentUser.getUsername()); - } + default User getUser(UserPrincipal currentUser) { + return getUserByName(currentUser.getUsername()); + } - default User getUserByName(String username) { - return findByUsername(username) - .orElseThrow(() -> new ResourceNotFoundException("User", "username", username)); - } + default User getUserByName(String username) { + return findByUsername(username) + .orElseThrow(() -> new ResourceNotFoundException("User", "username", username)); + } } diff --git a/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationEntryPoint.java b/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationEntryPoint.java deleted file mode 100644 index b9f644ab..00000000 --- a/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationEntryPoint.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.sopromadze.blogapi.security; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.stereotype.Component; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -@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."); - } -} diff --git a/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationFilter.java b/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationFilter.java deleted file mode 100644 index 3c162554..00000000 --- a/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationFilter.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.sopromadze.blogapi.security; - -import com.sopromadze.blogapi.service.CustomUserDetailsService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.util.StringUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -public class JwtAuthenticationFilter extends OncePerRequestFilter { - 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 deleted file mode 100644 index 77fad327..00000000 --- a/src/main/java/com/sopromadze/blogapi/security/JwtTokenProvider.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.sopromadze.blogapi.security; - -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; -import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Component; - -import java.util.Date; - -@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.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/service/AuthenticationService.java b/src/main/java/com/sopromadze/blogapi/service/AuthenticationService.java new file mode 100644 index 00000000..5145e8d9 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/service/AuthenticationService.java @@ -0,0 +1,44 @@ +package com.sopromadze.blogapi.service; + +import com.sopromadze.blogapi.model.user.User; +import com.sopromadze.blogapi.payload.LoginUserDto; +import com.sopromadze.blogapi.payload.RegisterUserDto; +import com.sopromadze.blogapi.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class AuthenticationService { + private final UserRepository userRepository; + + private final PasswordEncoder passwordEncoder; + + private final AuthenticationManager authenticationManager; + + public User signup(RegisterUserDto input) { + User user = User.builder() + .firstName(input.getFirstName()) + .lastName(input.getLastName()) + .email(input.getEmail()) + .username(input.getEmail()) + .password(passwordEncoder.encode(input.getPassword())) + .build(); + return userRepository.save(user); + } + + public User authenticate(LoginUserDto input) { + authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken( + input.getEmail(), + input.getPassword() + ) + ); + + return userRepository.findByEmail(input.getEmail()) + .orElseThrow(); + } +} diff --git a/src/main/java/com/sopromadze/blogapi/service/JwtService.java b/src/main/java/com/sopromadze/blogapi/service/JwtService.java new file mode 100644 index 00000000..55f33ebc --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/service/JwtService.java @@ -0,0 +1,84 @@ +package com.sopromadze.blogapi.service; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Service; + +import java.security.Key; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +@Service +public class JwtService { + @Value("${security.jwt.secret-key}") + private String secretKey; + + @Value("${security.jwt.expiration-time}") + private long jwtExpiration; + + public String extractUsername(String token) { + return extractClaim(token, Claims::getSubject); + } + + public T extractClaim(String token, Function claimsResolver) { + final Claims claims = extractAllClaims(token); + return claimsResolver.apply(claims); + } + + public String generateToken(UserDetails userDetails) { + return generateToken(new HashMap<>(), userDetails); + } + + public String generateToken(Map extraClaims, UserDetails userDetails) { + return buildToken(extraClaims, userDetails, jwtExpiration); + } + + public long getExpirationTime() { + return jwtExpiration; + } + + private String buildToken(Map extraClaims, UserDetails userDetails, long expiration) { + return Jwts + .builder() + .setClaims(extraClaims) + .setSubject(userDetails.getUsername()) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + expiration)) + .signWith(getSignInKey(), SignatureAlgorithm.HS256) + .compact(); + } + + public boolean isTokenValid(String token, UserDetails userDetails) { + final String username = extractUsername(token); + return (username.equals(userDetails.getUsername())) && !isTokenExpired(token); + } + + private boolean isTokenExpired(String token) { + return extractExpiration(token).before(new Date()); + } + + private Date extractExpiration(String token) { + return extractClaim(token, Claims::getExpiration); + } + + private Claims extractAllClaims(String token) { + return Jwts + .parserBuilder() + .setSigningKey(getSignInKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } + + private Key getSignInKey() { + byte[] keyBytes = Decoders.BASE64.decode(secretKey); + return Keys.hmacShaKeyFor(keyBytes); + } +} \ No newline at end of file diff --git a/src/main/java/com/sopromadze/blogapi/service/impl/CustomUserDetailsServiceImpl.java b/src/main/java/com/sopromadze/blogapi/service/impl/CustomUserDetailsServiceImpl.java index 27451513..7b36a096 100644 --- a/src/main/java/com/sopromadze/blogapi/service/impl/CustomUserDetailsServiceImpl.java +++ b/src/main/java/com/sopromadze/blogapi/service/impl/CustomUserDetailsServiceImpl.java @@ -10,26 +10,26 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import javax.transaction.Transactional; +import jakarta.transaction.Transactional; @Service public class CustomUserDetailsServiceImpl implements UserDetailsService, CustomUserDetailsService { - @Autowired - private UserRepository userRepository; + @Autowired + private UserRepository userRepository; - @Override - @Transactional - public UserDetails loadUserByUsername(String usernameOrEmail) { - 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); - } + @Override + @Transactional + public UserDetails loadUserByUsername(String usernameOrEmail) { + 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); + } - @Override - @Transactional - public UserDetails loadUserById(Long id) { - User user = userRepository.findById(id).orElseThrow(() -> new UsernameNotFoundException(String.format("User not found with id: %s", id))); + @Override + @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); - } + return UserPrincipal.create(user); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8b64d3c1..582dd2ad 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,12 @@ +logging: + level: + root: DEBUG + spring: datasource: url: jdbc:postgresql://localhost:5432/blogapi - username: postgres - password: postgres + username: blogapi_user + password: blogapi_pass jpa: hibernate: ddl-auto: none @@ -19,5 +23,10 @@ app: jwtSecret: secret jwtExpirationInMs: 3600000 +security: + jwt: + secret-key: 3cfa76ef14937c1c0ea519f8fc057a80fcd04a7420f8e8bcd0a7567c272e007b + expiration-time: 3600000 + cors: allowedOrings: '*' From bea4d2be61b7c83d7a68bee28e786113cca9e8fc Mon Sep 17 00:00:00 2001 From: Omari Sopromadze Date: Thu, 6 Jun 2024 17:34:53 +0200 Subject: [PATCH 3/3] feat: fix broken jwt auth --- LICENSE | 14 +- README.md | 135 ++++---- docker/init.sql | 39 ++- mvnw.cmd | 2 +- pom.xml | 41 ++- .../blogapi/BlogApiApplication.java | 3 +- .../blogapi/application/TodoUseCase.java | 23 ++ .../config/ApplicationConfiguration.java | 19 +- .../blogapi/config/AuditingConfig.java | 28 +- .../config/JwtAuthenticationFilter.java | 49 ++- .../sopromadze/blogapi/config/RoleSeeder.java | 42 +++ .../blogapi/config/SecurityConfig.java | 59 ---- .../blogapi/config/SecurityConfiguration.java | 41 +++ .../blogapi/config/WebMvcConfig.java | 23 -- .../blogapi/controller/AlbumController.java | 15 +- .../controller/AuthenticationController.java | 28 +- .../controller/CategoryController.java | 14 +- .../blogapi/controller/CommentController.java | 14 +- .../blogapi/controller/PhotoController.java | 15 +- .../blogapi/controller/PostController.java | 15 +- .../blogapi/controller/TagController.java | 17 +- .../blogapi/controller/TodoController.java | 95 ----- .../blogapi/controller/UserController.java | 35 +- .../sopromadze/blogapi/domain/model/Todo.java | 20 ++ .../domain/port/TodoPersistencePort.java | 9 + .../service/TodoPersistenceService.java | 23 ++ .../exception/AccessDeniedException.java | 53 +-- .../blogapi/exception/AppException.java | 16 +- .../exception/BadRequestException.java | 32 +- .../blogapi/exception/BlogapiException.java | 49 +-- .../exception/ResourceNotFoundException.java | 58 ++-- .../ResponseEntityErrorException.java | 18 +- .../RestControllerExceptionHandler.java | 189 +++++----- .../exception/UnauthorizedException.java | 53 +-- .../TodoPostgresqlPersistenceAdapter.java | 34 ++ .../postgresql/entity/TodoEntity.java | 36 ++ .../postgresql/mapper/TodoEntityMapper.java | 14 + .../postgresql/repository/TodoRepository.java | 14 + .../rest/controller/TodoController.java | 101 ++++++ .../rest/mapper/TodoMapper.java | 15 + .../payload/request/TodoPostRequestDto.java | 20 ++ .../payload/response/TodoResponseDto.java | 20 ++ .../com/sopromadze/blogapi/model/Album.java | 54 +-- .../sopromadze/blogapi/model/Category.java | 18 +- .../com/sopromadze/blogapi/model/Comment.java | 29 +- .../com/sopromadze/blogapi/model/Photo.java | 22 +- .../com/sopromadze/blogapi/model/Post.java | 35 +- .../com/sopromadze/blogapi/model/Tag.java | 7 +- .../com/sopromadze/blogapi/model/Todo.java | 48 --- .../blogapi/model/audit/DateAudit.java | 35 -- .../blogapi/model/audit/UserDateAudit.java | 28 -- .../model/metadata/MetadataEntity.java | 43 +++ .../sopromadze/blogapi/model/role/Role.java | 14 +- .../blogapi/model/role/RoleName.java | 4 +- .../blogapi/model/user/Address.java | 76 +--- .../blogapi/model/user/Company.java | 88 +---- .../sopromadze/blogapi/model/user/Geo.java | 74 +--- .../sopromadze/blogapi/model/user/User.java | 208 ----------- .../blogapi/model/user/UserEntity.java | 104 ++++++ .../blogapi/payload/AlbumResponse.java | 32 +- .../blogapi/payload/ApiResponse.java | 43 +-- .../blogapi/payload/CommentRequest.java | 5 +- .../blogapi/payload/DateAuditPayload.java | 29 +- .../blogapi/payload/ExceptionResponse.java | 54 +-- .../blogapi/payload/InfoRequest.java | 4 +- .../payload/JwtAuthenticationResponse.java | 12 +- .../blogapi/payload/LoginRequest.java | 5 +- .../blogapi/payload/LoginResponse.java | 2 + .../blogapi/payload/LoginUserDto.java | 3 + .../blogapi/payload/PagedResponse.java | 79 +++-- .../blogapi/payload/PhotoRequest.java | 4 +- .../blogapi/payload/PhotoResponse.java | 31 +- .../blogapi/payload/PostRequest.java | 4 +- .../blogapi/payload/PostResponse.java | 31 +- .../blogapi/payload/RegisterUserDto.java | 5 + .../blogapi/payload/SignUpRequest.java | 5 +- .../blogapi/payload/UserDateAuditPayload.java | 5 +- .../payload/UserIdentityAvailability.java | 3 +- .../blogapi/payload/UserProfile.java | 34 +- .../blogapi/payload/UserSummary.java | 13 +- .../blogapi/payload/request/AlbumRequest.java | 31 +- .../blogapi/repository/AlbumRepository.java | 4 +- .../blogapi/repository/CommentRepository.java | 4 +- .../blogapi/repository/PhotoRepository.java | 4 +- .../blogapi/repository/PostRepository.java | 10 +- .../blogapi/repository/RoleRepository.java | 4 +- .../blogapi/repository/TagRepository.java | 4 +- .../blogapi/repository/TodoRepository.java | 12 - .../blogapi/repository/UserRepository.java | 19 +- .../blogapi/security/CurrentUser.java | 9 +- .../blogapi/security/UserPrincipal.java | 170 ++++----- .../blogapi/service/AlbumService.java | 12 +- .../service/AuthenticationService.java | 36 +- .../blogapi/service/CategoryService.java | 12 +- .../blogapi/service/CommentService.java | 10 +- .../service/CustomUserDetailsService.java | 12 - .../blogapi/service/JwtService.java | 45 ++- .../blogapi/service/PhotoService.java | 12 +- .../blogapi/service/PostService.java | 16 +- .../blogapi/service/TagService.java | 10 +- .../blogapi/service/TodoService.java | 16 +- .../blogapi/service/UserService.java | 28 +- .../service/impl/AlbumServiceImpl.java | 143 ++++---- .../service/impl/CategoryServiceImpl.java | 105 +++--- .../service/impl/CommentServiceImpl.java | 200 +++++------ .../impl/CustomUserDetailsService.java | 44 +++ .../impl/CustomUserDetailsServiceImpl.java | 35 -- .../service/impl/PhotoServiceImpl.java | 214 ++++++------ .../blogapi/service/impl/PostServiceImpl.java | 249 +++++++------ .../blogapi/service/impl/TagServiceImpl.java | 105 +++--- .../blogapi/service/impl/TodoServiceImpl.java | 182 +++++----- .../blogapi/service/impl/UserServiceImpl.java | 327 +++++++++--------- .../blogapi/utils/AppConstants.java | 30 +- .../sopromadze/blogapi/utils/AppUtils.java | 24 +- .../blogapi/BlogApiApplicationTests.java | 16 - 115 files changed, 2405 insertions(+), 2579 deletions(-) create mode 100644 src/main/java/com/sopromadze/blogapi/application/TodoUseCase.java create mode 100644 src/main/java/com/sopromadze/blogapi/config/RoleSeeder.java delete mode 100644 src/main/java/com/sopromadze/blogapi/config/SecurityConfig.java create mode 100644 src/main/java/com/sopromadze/blogapi/config/SecurityConfiguration.java delete mode 100644 src/main/java/com/sopromadze/blogapi/config/WebMvcConfig.java delete mode 100644 src/main/java/com/sopromadze/blogapi/controller/TodoController.java create mode 100644 src/main/java/com/sopromadze/blogapi/domain/model/Todo.java create mode 100644 src/main/java/com/sopromadze/blogapi/domain/port/TodoPersistencePort.java create mode 100644 src/main/java/com/sopromadze/blogapi/domain/service/TodoPersistenceService.java create mode 100644 src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/adapter/TodoPostgresqlPersistenceAdapter.java create mode 100644 src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/entity/TodoEntity.java create mode 100644 src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/mapper/TodoEntityMapper.java create mode 100644 src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/repository/TodoRepository.java create mode 100644 src/main/java/com/sopromadze/blogapi/infrastructure/rest/controller/TodoController.java create mode 100644 src/main/java/com/sopromadze/blogapi/infrastructure/rest/mapper/TodoMapper.java create mode 100644 src/main/java/com/sopromadze/blogapi/infrastructure/rest/payload/request/TodoPostRequestDto.java create mode 100644 src/main/java/com/sopromadze/blogapi/infrastructure/rest/payload/response/TodoResponseDto.java delete mode 100644 src/main/java/com/sopromadze/blogapi/model/Todo.java delete mode 100644 src/main/java/com/sopromadze/blogapi/model/audit/DateAudit.java delete mode 100644 src/main/java/com/sopromadze/blogapi/model/audit/UserDateAudit.java create mode 100644 src/main/java/com/sopromadze/blogapi/model/metadata/MetadataEntity.java delete mode 100644 src/main/java/com/sopromadze/blogapi/model/user/User.java create mode 100644 src/main/java/com/sopromadze/blogapi/model/user/UserEntity.java delete mode 100644 src/main/java/com/sopromadze/blogapi/repository/TodoRepository.java delete mode 100644 src/main/java/com/sopromadze/blogapi/service/CustomUserDetailsService.java create mode 100644 src/main/java/com/sopromadze/blogapi/service/impl/CustomUserDetailsService.java delete mode 100644 src/main/java/com/sopromadze/blogapi/service/impl/CustomUserDetailsServiceImpl.java delete mode 100644 src/test/java/com/sopromadze/blogapi/BlogApiApplicationTests.java diff --git a/LICENSE b/LICENSE index 0ad25db4..cce8fa1f 100644 --- a/LICENSE +++ b/LICENSE @@ -85,16 +85,16 @@ 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 +parties to make or receive copies. Mere interaction with a userEntity through a computer network, with no transfer of a copy, is not conveying. - An interactive user interface displays "Appropriate Legal Notices" + An interactive userEntity 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 +tells the userEntity 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 +the interface presents a list of userEntity commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. @@ -215,7 +215,7 @@ terms of section 4, provided that you also meet all of these conditions: 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 + d) If the work has interactive userEntity 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. @@ -287,9 +287,9 @@ 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 +product received by a particular userEntity, "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 +of the particular userEntity or of the way in which the particular userEntity 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 diff --git a/README.md b/README.md index 2906f0fd..04978091 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,11 @@ git clone https://github.com/coma123/Spring-Boot-Blog-REST-API.git ``` **2. Create Mysql database** + ```bash create database blogapi ``` + - run `src/main/resources/blogapi.sql` **3. Change mysql username and password as per your installation** @@ -28,6 +30,7 @@ create database blogapi ```bash mvn spring-boot:run ``` + The app will start running at ## Explore Rest APIs @@ -36,86 +39,87 @@ The app defines following CRUD APIs. ### Auth -| Method | Url | Decription | Sample Valid Request Body | -| ------ | --- | ---------- | --------------------------- | -| POST | /api/auth/signup | Sign up | [JSON](#signup) | -| POST | /api/auth/signin | Log in | [JSON](#signin) | +| Method | Url | Decription | Sample Valid Request Body | +|--------|------------------|------------|---------------------------| +| POST | /api/auth/signup | Sign up | [JSON](#signup) | +| POST | /api/auth/signin | Log in | [JSON](#signin) | ### Users -| Method | Url | Description | Sample Valid Request Body | -| ------ | --- | ----------- | ------------------------- | -| GET | /api/users/me | Get logged in user profile | | -| GET | /api/users/{username}/profile | Get user profile by username | | -| GET | /api/users/{username}/posts | Get posts created by user | | -| GET | /api/users/{username}/albums | Get albums created by user | | -| GET | /api/users/checkUsernameAvailability | Check if username is available to register | | -| GET | /api/users/checkEmailAvailability | Check if email is available to register | | -| POST | /api/users | Add user (Only for admins) | [JSON](#usercreate) | -| PUT | /api/users/{username} | Update user (If profile belongs to logged in user or logged in user is admin) | [JSON](#userupdate) | -| DELETE | /api/users/{username} | Delete user (For logged in user or admin) | | -| PUT | /api/users/{username}/giveAdmin | Give admin role to user (only for admins) | | -| PUT | /api/users/{username}/TakeAdmin | Take admin role from user (only for admins) | | -| PUT | /api/users/setOrUpdateInfo | Update user profile (If profile belongs to logged in user or logged in user is admin) | [JSON](#userinfoupdate) | +| Method | Url | Description | Sample Valid Request Body | +|--------|--------------------------------------|---------------------------------------------------------------------------------------------------------|---------------------------| +| GET | /api/users/me | Get logged in userEntity profile | | +| GET | /api/users/{username}/profile | Get userEntity profile by username | | +| GET | /api/users/{username}/posts | Get posts created by userEntity | | +| GET | /api/users/{username}/albums | Get albums created by userEntity | | +| GET | /api/users/checkUsernameAvailability | Check if username is available to register | | +| GET | /api/users/checkEmailAvailability | Check if email is available to register | | +| POST | /api/users | Add userEntity (Only for admins) | [JSON](#usercreate) | +| PUT | /api/users/{username} | Update userEntity (If profile belongs to logged in userEntity or logged in userEntity is admin) | [JSON](#userupdate) | +| DELETE | /api/users/{username} | Delete userEntity (For logged in userEntity or admin) | | +| PUT | /api/users/{username}/giveAdmin | Give admin role to userEntity (only for admins) | | +| PUT | /api/users/{username}/TakeAdmin | Take admin role from userEntity (only for admins) | | +| PUT | /api/users/setOrUpdateInfo | Update userEntity profile (If profile belongs to logged in userEntity or logged in userEntity is admin) | [JSON](#userinfoupdate) | ### Posts -| Method | Url | Description | Sample Valid Request Body | -| ------ | --- | ----------- | ------------------------- | -| GET | /api/posts | Get all posts | | -| GET | /api/posts/{id} | Get post by id | | -| POST | /api/posts | Create new post (By logged in user) | [JSON](#postcreate) | -| PUT | /api/posts/{id} | Update post (If post belongs to logged in user or logged in user is admin) | [JSON](#postupdate) | -| DELETE | /api/posts/{id} | Delete post (If post belongs to logged in user or logged in user is admin) | | +| Method | Url | Description | Sample Valid Request Body | +|--------|-----------------|----------------------------------------------------------------------------------------|---------------------------| +| GET | /api/posts | Get all posts | | +| GET | /api/posts/{id} | Get post by id | | +| POST | /api/posts | Create new post (By logged in userEntity) | [JSON](#postcreate) | +| PUT | /api/posts/{id} | Update post (If post belongs to logged in userEntity or logged in userEntity is admin) | [JSON](#postupdate) | +| DELETE | /api/posts/{id} | Delete post (If post belongs to logged in userEntity or logged in userEntity is admin) | | ### Comments -| Method | Url | Description | Sample Valid Request Body | -| ------ | --- | ----------- | ------------------------- | -| GET | /api/posts/{postId}/comments | Get all comments which belongs to post with id = postId | | -| GET | /api/posts/{postId}/comments/{id} | Get comment by id if it belongs to post with id = postId | | -| POST | /api/posts/{postId}/comments | Create new comment for post with id = postId (By logged in user) | [JSON](#commentcreate) | -| PUT | /api/posts/{postId}/comments/{id} | Update comment by id if it belongs to post with id = postId (If comment belongs to logged in user or logged in user is admin) | [JSON](#commentupdate) | -| DELETE | /api/posts/{postId}/comments/{id} | Delete comment by id if it belongs to post with id = postId (If comment belongs to logged in user or logged in user is admin) | | +| Method | Url | Description | Sample Valid Request Body | +|--------|-----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|---------------------------| +| GET | /api/posts/{postId}/comments | Get all comments which belongs to post with id = postId | | +| GET | /api/posts/{postId}/comments/{id} | Get comment by id if it belongs to post with id = postId | | +| POST | /api/posts/{postId}/comments | Create new comment for post with id = postId (By logged in userEntity) | [JSON](#commentcreate) | +| PUT | /api/posts/{postId}/comments/{id} | Update comment by id if it belongs to post with id = postId (If comment belongs to logged in userEntity or logged in userEntity is admin) | [JSON](#commentupdate) | +| DELETE | /api/posts/{postId}/comments/{id} | Delete comment by id if it belongs to post with id = postId (If comment belongs to logged in userEntity or logged in userEntity is admin) | | ### Albums -| Method | Url | Description | Sample Valid Request Body | -| ------ | --- | ----------- | ------------------------- | -| GET | /api/albums | Get all albums | | -| GET | /api/albums/{id} | Get album by id | | -| POST | /api/albums | Create new album (By logged in user) | [JSON](#albumcreate) | -| PUT | /api/albums/{id} | Update album (If album belongs to logged in user or logged in user is admin) | [JSON](#albumupdate) | -| DELETE | /api/albums/{id} | Delete album (If album belongs to logged in user or logged in user is admin) | | -| GET | /api/albums/{id}/photos | Get all photos which belongs to album with id = id | | +| Method | Url | Description | Sample Valid Request Body | +|--------|-------------------------|------------------------------------------------------------------------------------------|---------------------------| +| GET | /api/albums | Get all albums | | +| GET | /api/albums/{id} | Get album by id | | +| POST | /api/albums | Create new album (By logged in userEntity) | [JSON](#albumcreate) | +| PUT | /api/albums/{id} | Update album (If album belongs to logged in userEntity or logged in userEntity is admin) | [JSON](#albumupdate) | +| DELETE | /api/albums/{id} | Delete album (If album belongs to logged in userEntity or logged in userEntity is admin) | | +| GET | /api/albums/{id}/photos | Get all photos which belongs to album with id = id | | ### Photos -| Method | Url | Description | Sample Valid Request Body | -| ------ | --- | ----------- | ------------------------- | -| GET | /api/photos | Get all photos | | -| GET | /api/photos/{id} | Get photo by id | | -| POST | /api/photos | Create new photo (By logged in user) | [JSON](#photocreate) | -| PUT | /api/photos/{id} | Update photo (If photo belongs to logged in user or logged in user is admin) | [JSON](#photoupdate) | -| DELETE | /api/photos/{id} | Delete photo (If photo belongs to logged in user or logged in user is admin) | | +| Method | Url | Description | Sample Valid Request Body | +|--------|------------------|------------------------------------------------------------------------------------------|---------------------------| +| GET | /api/photos | Get all photos | | +| GET | /api/photos/{id} | Get photo by id | | +| POST | /api/photos | Create new photo (By logged in userEntity) | [JSON](#photocreate) | +| PUT | /api/photos/{id} | Update photo (If photo belongs to logged in userEntity or logged in userEntity is admin) | [JSON](#photoupdate) | +| DELETE | /api/photos/{id} | Delete photo (If photo belongs to logged in userEntity or logged in userEntity is admin) | | ### Todos -| Method | Url | Description | Sample Valid Request Body | -| ------ | --- | ----------- | ------------------------- | -| GET | /api/todos | Get all todos which belongs to logged in user | | -| GET | /api/todos/{id} | Get todo by id (If todo belongs to logged in user) | | -| POST | /api/todos | Create new todo (By logged in user) | [JSON](#todocreate) | -| PUT | /api/todos/{id} | Update todo (If todo belongs to logged in user) | [JSON](#todoupdate) | -| DELETE | /api/todos/{id} | Delete todo (If todo belongs to logged in user) | | -| PUT | /api/todos/{id}/complete | Mark todo as complete (If todo belongs to logged in user) | | -| PUT | /api/todos/{id}/unComplete | Mark todo as uncomplete (If todo belongs to logged in user) | | +| Method | Url | Description | Sample Valid Request Body | +|--------|-----------------------------------|-------------------------------------------------------------------------------|---------------------------| +| GET | /api/todoEntities | Get all todoEntities which belongs to logged in userEntity | | +| GET | /api/todoEntities/{id} | Get todoEntity by id (If todoEntity belongs to logged in userEntity) | | +| POST | /api/todoEntities | Create new todoEntity (By logged in userEntity) | [JSON](#todocreate) | +| PUT | /api/todoEntities/{id} | Update todoEntity (If todoEntity belongs to logged in userEntity) | [JSON](#todoupdate) | +| DELETE | /api/todoEntities/{id} | Delete todoEntity (If todoEntity belongs to logged in userEntity) | | +| PUT | /api/todoEntities/{id}/complete | Mark todoEntity as complete (If todoEntity belongs to logged in userEntity) | | +| PUT | /api/todoEntities/{id}/unComplete | Mark todoEntity as uncomplete (If todoEntity belongs to logged in userEntity) | | Test them using postman or any other rest client. ## Sample Valid JSON Request Bodys ##### Sign Up -> /api/auth/signup + ```json { "firstName": "Leanne", @@ -127,6 +131,7 @@ Test them using postman or any other rest client. ``` ##### Log In -> /api/auth/signin + ```json { "usernameOrEmail": "leanne", @@ -135,6 +140,7 @@ Test them using postman or any other rest client. ``` ##### Create User -> /api/users + ```json { "firstName": "Ervin", @@ -163,6 +169,7 @@ Test them using postman or any other rest client. ``` ##### Update User -> /api/users/{username} + ```json { "firstName": "Ervin", @@ -191,6 +198,7 @@ Test them using postman or any other rest client. ``` ##### Update User Profile -> /api/users/setOrUpdateInfo + ```json { "street": "Douglas Extension", @@ -208,6 +216,7 @@ Test them using postman or any other rest client. ``` ##### Create Post -> /api/posts + ```json { "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", @@ -216,6 +225,7 @@ Test them using postman or any other rest client. ``` ##### Update Post -> /api/posts/{id} + ```json { "title": "UPDATED UPDATED UPDATED UPDATED UPDATED UPDATED", @@ -224,6 +234,7 @@ Test them using postman or any other rest client. ``` ##### Create Comment -> /api/posts/{postId}/comments + ```json { "body": "laudantium enim quasi est quidem magnam voluptate ipsam eos tempora quo necessitatibus dolor quam autem quasi reiciendis et nam sapiente accusantium" @@ -231,6 +242,7 @@ Test them using postman or any other rest client. ``` ##### Update Comment -> /api/posts/{postId}/comments/{id} + ```json { "body": "UPDATED UPDATED UPDATED UPDATED UPDATED UPDATED UPDATED UPDATED UPDATED UPDATED " @@ -238,6 +250,7 @@ Test them using postman or any other rest client. ``` ##### Create Album -> /api/albums + ```json { "title": "quidem molestiae enim" @@ -245,6 +258,7 @@ Test them using postman or any other rest client. ``` ##### Update Album -> /api/albums/{id} + ```json { "title": "quidem molestiae enim UPDATED" @@ -252,6 +266,7 @@ Test them using postman or any other rest client. ``` ##### Create Photo -> /api/photos + ```json { "title": "accusamus beatae ad facilis cum similique qui sunt", @@ -262,6 +277,7 @@ Test them using postman or any other rest client. ``` ##### Update Photo -> /api/photos{id} + ```json { "title": "accusamus beatae ad facilis ", @@ -271,7 +287,8 @@ Test them using postman or any other rest client. } ``` -##### Create Todo -> /api/todos +##### Create Todo -> /api/todoEntities + ```json { "title": "delectus aut autem", @@ -279,11 +296,13 @@ Test them using postman or any other rest client. } ``` -##### Update Todo -> /api/todos{id} +##### Update Todo -> /api/todoEntities{id} + ```json { "title": "delectus aut autem Updated", "completed": true } ``` + ![segment](https://api.segment.io/v1/pixel/track?data=ewogICJ3cml0ZUtleSI6ICJwcDJuOTU4VU1NT21NR090MWJXS0JQd0tFNkcydW51OCIsCiAgInVzZXJJZCI6ICIxMjNibG9nYXBpMTIzIiwKICAiZXZlbnQiOiAiQmxvZ0FwaSB2aXNpdGVkIiwKICAicHJvcGVydGllcyI6IHsKICAgICJzdWJqZWN0IjogIkJsb2dBcGkgdmlzaXRlZCIsCiAgICAiZW1haWwiOiAiY29tcy5zcHVyc0BnbWFpbC5jb20iCiAgfQp9) diff --git a/docker/init.sql b/docker/init.sql index 03c0cc16..cc7fc32b 100644 --- a/docker/init.sql +++ b/docker/init.sql @@ -77,12 +77,14 @@ CREATE TABLE IF NOT EXISTS users company_id bigint check (company_id > 0) DEFAULT NULL REFERENCES company (id), created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by bigint, + updated_by bigint, PRIMARY KEY (id) ); CREATE TABLE IF NOT EXISTS todos ( - id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + todo_id bigint check (todo_id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, title varchar(255) NOT NULL, completed boolean default false, user_id bigint check (user_id > 0) DEFAULT NULL REFERENCES users (id), @@ -90,7 +92,7 @@ CREATE TABLE IF NOT EXISTS todos updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, created_by bigint check (created_by > 0) NOT NULL, updated_by bigint check (updated_by > 0) NOT NULL, - PRIMARY KEY (id) + PRIMARY KEY (todo_id) ); CREATE TABLE IF NOT EXISTS albums @@ -119,16 +121,29 @@ CREATE TABLE photos PRIMARY KEY (id) ); +CREATE TABLE IF NOT EXISTS categories +( + category_id bigint check (category_id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + name varchar(255) NOT NULL, + user_id bigint check (user_id > 0) DEFAULT NULL REFERENCES users (id), + created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by bigint check (created_by > 0) NOT NULL, + updated_by bigint check (updated_by > 0) NOT NULL, + PRIMARY KEY (category_id) +); + CREATE TABLE posts ( - id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, - title varchar(255) NOT NULL, - body text NOT NULL, - user_id bigint check (user_id > 0) DEFAULT NULL REFERENCES users (id), - created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by bigint check (created_by > 0) NOT NULL, - updated_by bigint check (updated_by > 0) NOT NULL, + id bigint check (id > 0) NOT NULL GENERATED ALWAYS AS IDENTITY, + title varchar(255) NOT NULL, + body text NOT NULL, + user_id bigint check (user_id > 0) DEFAULT NULL REFERENCES users (id), + category_id bigint check (category_id > 0) DEFAULT NULL REFERENCES categories (category_id), + created_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by bigint check (created_by > 0) NOT NULL, + updated_by bigint check (updated_by > 0) NOT NULL, PRIMARY KEY (id) ); @@ -169,7 +184,3 @@ CREATE TABLE user_role role_id bigint check ( role_id > 0 ) NOT NULL REFERENCES roles (id), PRIMARY KEY (id) ); - -INSERT INTO roles(name) -VALUES ('ROLE_ADMIN'), - ('ROLE_USER'); diff --git a/mvnw.cmd b/mvnw.cmd index 019bd74d..2c9a787c 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -41,7 +41,7 @@ @REM set %HOME% to equivalent of $HOME if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") -@REM Execute a user defined script before this one +@REM Execute a userEntity defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" diff --git a/pom.xml b/pom.xml index ebc8cd1e..e74641e8 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,7 @@ UTF-8 UTF-8 21 + 1.5.5.Final coma123 https://sonarcloud.io @@ -64,7 +65,6 @@ org.projectlombok lombok - 1.18.32 provided @@ -76,7 +76,6 @@ test - org.springframework.security spring-security-test @@ -84,14 +83,12 @@ - org.hamcrest hamcrest-library test - org.modelmapper modelmapper @@ -125,20 +122,26 @@ [2.3.5,) + + org.mapstruct + mapstruct + ${mapstruct.version} + + io.jsonwebtoken jjwt-api - 0.11.5 + 0.12.5 io.jsonwebtoken jjwt-impl - 0.11.5 + 0.12.5 io.jsonwebtoken jjwt-jackson - 0.11.5 + 0.12.5 @@ -169,6 +172,30 @@ + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + + + org.projectlombok + lombok + ${lombok.version} + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + diff --git a/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java b/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java index 0329c2c7..5fc2ac79 100644 --- a/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java +++ b/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java @@ -11,8 +11,7 @@ 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) { diff --git a/src/main/java/com/sopromadze/blogapi/application/TodoUseCase.java b/src/main/java/com/sopromadze/blogapi/application/TodoUseCase.java new file mode 100644 index 00000000..9b11479b --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/application/TodoUseCase.java @@ -0,0 +1,23 @@ +package com.sopromadze.blogapi.application; + +import com.sopromadze.blogapi.domain.model.Todo; +import com.sopromadze.blogapi.domain.service.TodoPersistenceService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@RequiredArgsConstructor +@Component +public class TodoUseCase { + + private final TodoPersistenceService todoPersistenceService; + + public Todo createTodo(Todo todo, String creatorUsername) { + log.debug("[START insert] todo: {} creatorUsername: {}", todo, creatorUsername); + Todo response = todoPersistenceService.createTodo(todo, creatorUsername); + log.debug("[STOP insert] inserted: {}", response); + return response; + } + +} diff --git a/src/main/java/com/sopromadze/blogapi/config/ApplicationConfiguration.java b/src/main/java/com/sopromadze/blogapi/config/ApplicationConfiguration.java index 074aef8c..f2a25ad9 100644 --- a/src/main/java/com/sopromadze/blogapi/config/ApplicationConfiguration.java +++ b/src/main/java/com/sopromadze/blogapi/config/ApplicationConfiguration.java @@ -1,7 +1,5 @@ package com.sopromadze.blogapi.config; -import com.sopromadze.blogapi.repository.UserRepository; -import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; @@ -9,19 +7,10 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @Configuration -@RequiredArgsConstructor public class ApplicationConfiguration { - private final UserRepository userRepository; - - @Bean - public UserDetailsService userDetailsService() { - return username -> userRepository.findByEmail(username) - .orElseThrow(() -> new UsernameNotFoundException("User not found")); - } @Bean public BCryptPasswordEncoder passwordEncoder() { @@ -34,12 +23,12 @@ public AuthenticationManager authenticationManager(AuthenticationConfiguration c } @Bean - AuthenticationProvider authenticationProvider() { - DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + public AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService, BCryptPasswordEncoder passwordEncoder) { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(passwordEncoder); - authProvider.setUserDetailsService(userDetailsService()); - authProvider.setPasswordEncoder(passwordEncoder()); + authProvider.setUserDetailsService(userDetailsService); return authProvider; } + } diff --git a/src/main/java/com/sopromadze/blogapi/config/AuditingConfig.java b/src/main/java/com/sopromadze/blogapi/config/AuditingConfig.java index 144a9444..a4b988be 100644 --- a/src/main/java/com/sopromadze/blogapi/config/AuditingConfig.java +++ b/src/main/java/com/sopromadze/blogapi/config/AuditingConfig.java @@ -15,24 +15,26 @@ @EnableJpaAuditing public class AuditingConfig { - @Bean - public AuditorAware auditorProvider() { - return new SpringSecurityAuditAwareImpl(); - } + @Bean + public AuditorAware auditorProvider() { + return new SpringSecurityAuditAwareImpl(); + } + } 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/JwtAuthenticationFilter.java b/src/main/java/com/sopromadze/blogapi/config/JwtAuthenticationFilter.java index 894fb802..49f602cb 100644 --- a/src/main/java/com/sopromadze/blogapi/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/sopromadze/blogapi/config/JwtAuthenticationFilter.java @@ -6,7 +6,6 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; -import org.springframework.lang.NonNull; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -15,7 +14,6 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.servlet.HandlerExceptionResolver; import java.io.IOException; @@ -23,47 +21,42 @@ @RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter { - private final HandlerExceptionResolver handlerExceptionResolver; private final JwtService jwtService; + private final UserDetailsService userDetailsService; @Override - protected void doFilterInternal( - @NonNull HttpServletRequest request, - @NonNull HttpServletResponse response, - @NonNull FilterChain filterChain - ) throws ServletException, IOException { - final String authHeader = request.getHeader("Authorization"); + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + String authHeader = request.getHeader("Authorization"); if (authHeader == null || !authHeader.startsWith("Bearer ")) { filterChain.doFilter(request, response); return; } - try { - final String jwt = authHeader.substring(7); - final String userEmail = jwtService.extractUsername(jwt); + String jwt = authHeader.substring(7); + String username = jwtService.extractUsername(jwt); - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (userEmail != null && authentication == null) { - UserDetails userDetails = userDetailsService.loadUserByUsername(userEmail); + if (username != null && authentication == null) { + UserDetails userDetails = userDetailsService.loadUserByUsername(username); - if (jwtService.isTokenValid(jwt, userDetails)) { - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( - userDetails, - null, - userDetails.getAuthorities() - ); + if (jwtService.isTokenValid(jwt, userDetails)) { + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( + userDetails, + null, + userDetails.getAuthorities() + ); - authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - SecurityContextHolder.getContext().setAuthentication(authToken); - } + authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authToken); } - - filterChain.doFilter(request, response); - } catch (Exception exception) { - handlerExceptionResolver.resolveException(request, response, null, exception); } + + filterChain.doFilter(request, response); + } + } diff --git a/src/main/java/com/sopromadze/blogapi/config/RoleSeeder.java b/src/main/java/com/sopromadze/blogapi/config/RoleSeeder.java new file mode 100644 index 00000000..b643b7aa --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/config/RoleSeeder.java @@ -0,0 +1,42 @@ +package com.sopromadze.blogapi.config; + +import com.sopromadze.blogapi.model.role.Role; +import com.sopromadze.blogapi.model.role.RoleName; +import com.sopromadze.blogapi.repository.RoleRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.Optional; + +@Component +@Slf4j +@RequiredArgsConstructor +public class RoleSeeder implements ApplicationListener { + + private final RoleRepository roleRepository; + + @Override + public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { + loadRoles(); + } + + private void loadRoles() { + RoleName[] roleNames = new RoleName[] { RoleName.ROLE_ADMIN, RoleName.ROLE_USER }; + + Arrays.stream(roleNames).forEach(roleName -> { + Optional optionalRole = roleRepository.findByName(roleName); + + optionalRole.ifPresentOrElse(role -> log.info("Role {} already exists", role.getName()), + () -> { + Role roleToCreate = new Role(roleName); + roleRepository.save(roleToCreate); + log.info("Role {} created", roleToCreate.getName()); + }); + }); + } + +} diff --git a/src/main/java/com/sopromadze/blogapi/config/SecurityConfig.java b/src/main/java/com/sopromadze/blogapi/config/SecurityConfig.java deleted file mode 100644 index e426ba98..00000000 --- a/src/main/java/com/sopromadze/blogapi/config/SecurityConfig.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.sopromadze.blogapi.config; - -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; - -import java.util.List; - -@Configuration -@EnableWebSecurity -@RequiredArgsConstructor -public class SecurityConfig { - private final AuthenticationProvider authenticationProvider; - private final JwtAuthenticationFilter jwtAuthenticationFilter; - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http - .csrf(AbstractHttpConfigurer::disable) - .authorizeHttpRequests() - .requestMatchers(HttpMethod.GET, "/api/**").permitAll() - .requestMatchers(HttpMethod.POST, "/api/auth/**").permitAll() - .requestMatchers(HttpMethod.GET, "/api/users/checkUsernameAvailability", "/api/users/checkEmailAvailability").permitAll() - .anyRequest() - .authenticated() - .and() - .sessionManagement( - config -> config.sessionCreationPolicy(SessionCreationPolicy.STATELESS) - ) - .authenticationProvider(authenticationProvider) - .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); - - return http.build(); - } - - @Bean - public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - - configuration.setAllowedOrigins(List.of("http://localhost:8080")); - configuration.setAllowedMethods(List.of("GET", "POST")); - - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); - - return source; - } -} diff --git a/src/main/java/com/sopromadze/blogapi/config/SecurityConfiguration.java b/src/main/java/com/sopromadze/blogapi/config/SecurityConfiguration.java new file mode 100644 index 00000000..5c4e93cd --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/config/SecurityConfiguration.java @@ -0,0 +1,41 @@ +package com.sopromadze.blogapi.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@EnableWebSecurity +@EnableMethodSecurity +@RequiredArgsConstructor +public class SecurityConfiguration { + + private final AuthenticationProvider authenticationProvider; + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) + .cors(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(auth -> { + auth.requestMatchers("/api/auth/**").permitAll(); + auth.anyRequest().authenticated(); + }) + .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authenticationProvider(authenticationProvider) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + +} diff --git a/src/main/java/com/sopromadze/blogapi/config/WebMvcConfig.java b/src/main/java/com/sopromadze/blogapi/config/WebMvcConfig.java deleted file mode 100644 index 301ecb17..00000000 --- a/src/main/java/com/sopromadze/blogapi/config/WebMvcConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -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 { - - @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 46edab2e..cab58d6d 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java @@ -13,26 +13,17 @@ import com.sopromadze.blogapi.service.PhotoService; import com.sopromadze.blogapi.utils.AppConstants; import com.sopromadze.blogapi.utils.AppUtils; +import jakarta.validation.Valid; 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.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 jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/albums") public class AlbumController { + @Autowired private AlbumService albumService; diff --git a/src/main/java/com/sopromadze/blogapi/controller/AuthenticationController.java b/src/main/java/com/sopromadze/blogapi/controller/AuthenticationController.java index e1eef70a..643c63bb 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/AuthenticationController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/AuthenticationController.java @@ -1,11 +1,13 @@ package com.sopromadze.blogapi.controller; -import com.sopromadze.blogapi.model.user.User; +import com.sopromadze.blogapi.model.user.UserEntity; import com.sopromadze.blogapi.payload.LoginResponse; import com.sopromadze.blogapi.payload.LoginUserDto; import com.sopromadze.blogapi.payload.RegisterUserDto; import com.sopromadze.blogapi.service.AuthenticationService; import com.sopromadze.blogapi.service.JwtService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -14,34 +16,34 @@ @RequestMapping("/api/auth") @RestController +@RequiredArgsConstructor public class AuthenticationController { + private final JwtService jwtService; private final AuthenticationService authenticationService; - public AuthenticationController(JwtService jwtService, AuthenticationService authenticationService) { - this.jwtService = jwtService; - this.authenticationService = authenticationService; - } - @PostMapping("/signup") - public ResponseEntity register(@RequestBody RegisterUserDto registerUserDto) { - User registeredUser = authenticationService.signup(registerUserDto); + public ResponseEntity register(@RequestBody RegisterUserDto registerUserDto) { + UserEntity registeredUserEntity = authenticationService.signup(registerUserDto); - return ResponseEntity.ok(registeredUser); + return ResponseEntity.status(HttpStatus.CREATED) + .body(registeredUserEntity); } @PostMapping("/login") public ResponseEntity authenticate(@RequestBody LoginUserDto loginUserDto) { - User authenticatedUser = authenticationService.authenticate(loginUserDto); + UserEntity authenticatedUserEntity = authenticationService.authenticate(loginUserDto); - String jwtToken = jwtService.generateToken(authenticatedUser); + String jwtToken = jwtService.generateToken(authenticatedUserEntity.getUsername()); LoginResponse loginResponse = LoginResponse.builder() .token(jwtToken) - .expiresIn(jwtService.getExpirationTime()) + .expiresIn(jwtService.getJwtExpiration()) .build(); - return ResponseEntity.ok(loginResponse); + return ResponseEntity.status(HttpStatus.OK) + .body(loginResponse); } + } diff --git a/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java b/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java index c56741ad..1a74198d 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java @@ -8,24 +8,16 @@ import com.sopromadze.blogapi.security.UserPrincipal; import com.sopromadze.blogapi.service.CategoryService; import com.sopromadze.blogapi.utils.AppConstants; +import jakarta.validation.Valid; 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 jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/categories") public class CategoryController { + @Autowired private CategoryService categoryService; diff --git a/src/main/java/com/sopromadze/blogapi/controller/CommentController.java b/src/main/java/com/sopromadze/blogapi/controller/CommentController.java index b4fc98f9..1f07fe94 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/CommentController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/CommentController.java @@ -8,25 +8,17 @@ import com.sopromadze.blogapi.security.UserPrincipal; import com.sopromadze.blogapi.service.CommentService; import com.sopromadze.blogapi.utils.AppConstants; +import jakarta.validation.Valid; 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 jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/posts/{postId}/comments") public class CommentController { + @Autowired private CommentService commentService; diff --git a/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java b/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java index d6f8d99f..2b400621 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java @@ -8,25 +8,17 @@ import com.sopromadze.blogapi.security.UserPrincipal; import com.sopromadze.blogapi.service.PhotoService; import com.sopromadze.blogapi.utils.AppConstants; +import jakarta.validation.Valid; 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 jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/photos") public class PhotoController { + @Autowired private PhotoService photoService; @@ -70,4 +62,5 @@ public ResponseEntity deletePhoto(@PathVariable(name = "id") Long i 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 9f5c15cf..71c6d4c7 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/PostController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/PostController.java @@ -9,25 +9,17 @@ import com.sopromadze.blogapi.security.UserPrincipal; import com.sopromadze.blogapi.service.PostService; import com.sopromadze.blogapi.utils.AppConstants; +import jakarta.validation.Valid; 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 jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/posts") public class PostController { + @Autowired private PostService postService; @@ -92,4 +84,5 @@ public ResponseEntity deletePost(@PathVariable(name = "id") Long id 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 index 3481f9e9..bfa0b58b 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/TagController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/TagController.java @@ -7,25 +7,17 @@ import com.sopromadze.blogapi.security.UserPrincipal; import com.sopromadze.blogapi.service.TagService; import com.sopromadze.blogapi.utils.AppConstants; +import jakarta.validation.Valid; 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 jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/tags") public class TagController { + @Autowired private TagService tagService; @@ -56,7 +48,8 @@ public ResponseEntity getTag(@PathVariable(name = "id") Long id) { @PutMapping("/{id}") @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") - public ResponseEntity updateTag(@PathVariable(name = "id") Long id, @Valid @RequestBody Tag tag, @CurrentUser UserPrincipal currentUser) { + public ResponseEntity updateTag(@PathVariable(name = "id") Long id, @Valid @RequestBody Tag tag, + @CurrentUser UserPrincipal currentUser) { Tag updatedTag = tagService.updateTag(id, tag, currentUser); diff --git a/src/main/java/com/sopromadze/blogapi/controller/TodoController.java b/src/main/java/com/sopromadze/blogapi/controller/TodoController.java deleted file mode 100644 index 6f9fd512..00000000 --- a/src/main/java/com/sopromadze/blogapi/controller/TodoController.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.sopromadze.blogapi.controller; - -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.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 jakarta.validation.Valid; - -@RestController -@RequestMapping("/api/todos") -public class TodoController { - - @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 65bfdc68..97e5526f 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/UserController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/UserController.java @@ -2,38 +2,25 @@ import com.sopromadze.blogapi.model.Album; import com.sopromadze.blogapi.model.Post; -import com.sopromadze.blogapi.model.user.User; -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.model.user.UserEntity; +import com.sopromadze.blogapi.payload.*; 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.utils.AppConstants; +import jakarta.validation.Valid; 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 jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/users") public class UserController { + @Autowired private UserService userService; @@ -92,17 +79,17 @@ public ResponseEntity> getUserAlbums(@PathVariable(name = " @PostMapping @PreAuthorize("hasRole('ADMIN')") - public ResponseEntity addUser(@Valid @RequestBody User user) { - User newUser = userService.addUser(user); + public ResponseEntity addUser(@Valid @RequestBody UserEntity userEntity) { + UserEntity newUserEntity = userService.addUser(userEntity); - return new ResponseEntity<>(newUser, HttpStatus.CREATED); + return new ResponseEntity<>(newUserEntity, 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); + public ResponseEntity updateUser(@Valid @RequestBody UserEntity newUserEntity, + @PathVariable(value = "username") String username, @CurrentUser UserPrincipal currentUser) { + UserEntity updatedUSer = userService.updateUser(newUserEntity, username, currentUser); return new ResponseEntity<>(updatedUSer, HttpStatus.CREATED); } diff --git a/src/main/java/com/sopromadze/blogapi/domain/model/Todo.java b/src/main/java/com/sopromadze/blogapi/domain/model/Todo.java new file mode 100644 index 00000000..2c12332e --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/domain/model/Todo.java @@ -0,0 +1,20 @@ +package com.sopromadze.blogapi.domain.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Todo { + + private Long todoId; + + private String title; + + private boolean completed; + +} diff --git a/src/main/java/com/sopromadze/blogapi/domain/port/TodoPersistencePort.java b/src/main/java/com/sopromadze/blogapi/domain/port/TodoPersistencePort.java new file mode 100644 index 00000000..7427c423 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/domain/port/TodoPersistencePort.java @@ -0,0 +1,9 @@ +package com.sopromadze.blogapi.domain.port; + +import com.sopromadze.blogapi.domain.model.Todo; + +public interface TodoPersistencePort { + + Todo insert(Todo todo, String creatorUsername); + +} diff --git a/src/main/java/com/sopromadze/blogapi/domain/service/TodoPersistenceService.java b/src/main/java/com/sopromadze/blogapi/domain/service/TodoPersistenceService.java new file mode 100644 index 00000000..e727a007 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/domain/service/TodoPersistenceService.java @@ -0,0 +1,23 @@ +package com.sopromadze.blogapi.domain.service; + +import com.sopromadze.blogapi.domain.model.Todo; +import com.sopromadze.blogapi.domain.port.TodoPersistencePort; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@RequiredArgsConstructor +@Service +public class TodoPersistenceService { + + private final TodoPersistencePort todoPersistencePort; + + public Todo createTodo(Todo todo, String creatorUsername) { + log.debug("[START insert] todo: {} creatorUsername: {}", todo, creatorUsername); + Todo response = todoPersistencePort.insert(todo, creatorUsername); + log.debug("[STOP insert] inserted: {}", response); + return response; + } + +} diff --git a/src/main/java/com/sopromadze/blogapi/exception/AccessDeniedException.java b/src/main/java/com/sopromadze/blogapi/exception/AccessDeniedException.java index ba5391e1..725c09eb 100644 --- a/src/main/java/com/sopromadze/blogapi/exception/AccessDeniedException.java +++ b/src/main/java/com/sopromadze/blogapi/exception/AccessDeniedException.java @@ -6,40 +6,41 @@ @ResponseStatus(code = HttpStatus.UNAUTHORIZED) public class AccessDeniedException extends RuntimeException { - private static final long serialVersionUID = 1L; - private ApiResponse apiResponse; + private static final long serialVersionUID = 1L; - private String message; + private ApiResponse apiResponse; - public AccessDeniedException(ApiResponse apiResponse) { - super(); - this.apiResponse = apiResponse; - } + private String message; - public AccessDeniedException(String message) { - super(message); - this.message = message; - } + public AccessDeniedException(ApiResponse apiResponse) { + super(); + this.apiResponse = apiResponse; + } - public AccessDeniedException(String message, Throwable cause) { - super(message, cause); - } + public AccessDeniedException(String message) { + super(message); + this.message = message; + } - public ApiResponse getApiResponse() { - return apiResponse; - } + public AccessDeniedException(String message, Throwable cause) { + super(message, cause); + } - public void setApiResponse(ApiResponse apiResponse) { - this.apiResponse = apiResponse; - } + public ApiResponse getApiResponse() { + return apiResponse; + } - public String getMessage() { - return message; - } + public void setApiResponse(ApiResponse apiResponse) { + this.apiResponse = apiResponse; + } - public void setMessage(String message) { - this.message = message; - } + 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 a213876d..d22046db 100644 --- a/src/main/java/com/sopromadze/blogapi/exception/AppException.java +++ b/src/main/java/com/sopromadze/blogapi/exception/AppException.java @@ -5,13 +5,15 @@ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public class AppException extends RuntimeException { - private static final long serialVersionUID = 1L; - public AppException(String message) { - super(message); - } + private static final long serialVersionUID = 1L; + + public AppException(String message) { + super(message); + } + + public AppException(String message, Throwable cause) { + super(message, cause); + } - 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 a35149fe..9389dd02 100644 --- a/src/main/java/com/sopromadze/blogapi/exception/BadRequestException.java +++ b/src/main/java/com/sopromadze/blogapi/exception/BadRequestException.java @@ -6,24 +6,26 @@ @ResponseStatus(HttpStatus.BAD_REQUEST) public class BadRequestException extends RuntimeException { - private static final long serialVersionUID = 1L; - private ApiResponse apiResponse; + private static final long serialVersionUID = 1L; - public BadRequestException(ApiResponse apiResponse) { - super(); - this.apiResponse = apiResponse; - } + private ApiResponse apiResponse; - public BadRequestException(String message) { - super(message); - } + public BadRequestException(ApiResponse apiResponse) { + super(); + this.apiResponse = apiResponse; + } - public BadRequestException(String message, Throwable cause) { - super(message, cause); - } + public BadRequestException(String message) { + super(message); + } + + public BadRequestException(String message, Throwable cause) { + super(message, cause); + } + + public ApiResponse getApiResponse() { + return apiResponse; + } - 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 index 4da4fd63..7d979b9e 100644 --- a/src/main/java/com/sopromadze/blogapi/exception/BlogapiException.java +++ b/src/main/java/com/sopromadze/blogapi/exception/BlogapiException.java @@ -4,29 +4,30 @@ 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; - } + 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 4dd90e07..5ac1b817 100644 --- a/src/main/java/com/sopromadze/blogapi/exception/ResourceNotFoundException.java +++ b/src/main/java/com/sopromadze/blogapi/exception/ResourceNotFoundException.java @@ -6,40 +6,44 @@ @ResponseStatus(value = HttpStatus.NOT_FOUND) public class ResourceNotFoundException extends RuntimeException { - private static final long serialVersionUID = 1L; - private transient ApiResponse apiResponse; + private static final long serialVersionUID = 1L; - private String resourceName; - private String fieldName; - private Object fieldValue; + private transient ApiResponse apiResponse; - public ResourceNotFoundException(String resourceName, String fieldName, Object fieldValue) { - super(); - this.resourceName = resourceName; - this.fieldName = fieldName; - this.fieldValue = fieldValue; - } + private String resourceName; - public String getResourceName() { - return resourceName; - } + private String fieldName; - public String getFieldName() { - return fieldName; - } + private Object fieldValue; - public Object getFieldValue() { - return fieldValue; - } + public ResourceNotFoundException(String resourceName, String fieldName, Object fieldValue) { + super(); + this.resourceName = resourceName; + this.fieldName = fieldName; + this.fieldValue = fieldValue; + } - public ApiResponse getApiResponse() { - return apiResponse; - } + public String getResourceName() { + return resourceName; + } - private void setApiResponse() { - String message = String.format("%s not found with %s: '%s'", resourceName, fieldName, fieldValue); + 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); + } - 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 index f5aeaaed..b6b76e43 100644 --- a/src/main/java/com/sopromadze/blogapi/exception/ResponseEntityErrorException.java +++ b/src/main/java/com/sopromadze/blogapi/exception/ResponseEntityErrorException.java @@ -4,15 +4,17 @@ import org.springframework.http.ResponseEntity; public class ResponseEntityErrorException extends RuntimeException { - private static final long serialVersionUID = -3156815846745801694L; - private transient ResponseEntity apiResponse; + private static final long serialVersionUID = -3156815846745801694L; - public ResponseEntityErrorException(ResponseEntity apiResponse) { - this.apiResponse = apiResponse; - } + private transient ResponseEntity apiResponse; + + public ResponseEntityErrorException(ResponseEntity apiResponse) { + this.apiResponse = apiResponse; + } + + public ResponseEntity getApiResponse() { + return 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 a9372e45..14460689 100644 --- a/src/main/java/com/sopromadze/blogapi/exception/RestControllerExceptionHandler.java +++ b/src/main/java/com/sopromadze/blogapi/exception/RestControllerExceptionHandler.java @@ -21,98 +21,99 @@ @ControllerAdvice public class RestControllerExceptionHandler { - 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); - } + 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 index 1760c66b..acc59dbc 100644 --- a/src/main/java/com/sopromadze/blogapi/exception/UnauthorizedException.java +++ b/src/main/java/com/sopromadze/blogapi/exception/UnauthorizedException.java @@ -6,40 +6,41 @@ @ResponseStatus(code = HttpStatus.UNAUTHORIZED) public class UnauthorizedException extends RuntimeException { - private static final long serialVersionUID = 1L; - private ApiResponse apiResponse; + private static final long serialVersionUID = 1L; - private String message; + private ApiResponse apiResponse; - public UnauthorizedException(ApiResponse apiResponse) { - super(); - this.apiResponse = apiResponse; - } + private String message; - public UnauthorizedException(String message) { - super(message); - this.message = message; - } + public UnauthorizedException(ApiResponse apiResponse) { + super(); + this.apiResponse = apiResponse; + } - public UnauthorizedException(String message, Throwable cause) { - super(message, cause); - } + public UnauthorizedException(String message) { + super(message); + this.message = message; + } - public ApiResponse getApiResponse() { - return apiResponse; - } + public UnauthorizedException(String message, Throwable cause) { + super(message, cause); + } - public void setApiResponse(ApiResponse apiResponse) { - this.apiResponse = apiResponse; - } + public ApiResponse getApiResponse() { + return apiResponse; + } - public String getMessage() { - return message; - } + public void setApiResponse(ApiResponse apiResponse) { + this.apiResponse = apiResponse; + } - public void setMessage(String message) { - this.message = message; - } + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } } diff --git a/src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/adapter/TodoPostgresqlPersistenceAdapter.java b/src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/adapter/TodoPostgresqlPersistenceAdapter.java new file mode 100644 index 00000000..f1073c7b --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/adapter/TodoPostgresqlPersistenceAdapter.java @@ -0,0 +1,34 @@ +package com.sopromadze.blogapi.infrastructure.persistence.postgresql.adapter; + +import com.sopromadze.blogapi.domain.model.Todo; +import com.sopromadze.blogapi.domain.port.TodoPersistencePort; +import com.sopromadze.blogapi.infrastructure.persistence.postgresql.entity.TodoEntity; +import com.sopromadze.blogapi.infrastructure.persistence.postgresql.mapper.TodoEntityMapper; +import com.sopromadze.blogapi.infrastructure.persistence.postgresql.repository.TodoRepository; +import com.sopromadze.blogapi.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class TodoPostgresqlPersistenceAdapter implements TodoPersistencePort { + + private final TodoEntityMapper todoEntityMapper; + + private final TodoRepository todoRepository; + + private final UserRepository userRepository; + + @Override + public Todo insert(Todo todo, String creatorUsername) { + TodoEntity todoEntity = todoEntityMapper.toEntity(todo); + userRepository.findByUsername(creatorUsername) + .ifPresent(todoEntity::setUser); + TodoEntity savedTodo = todoRepository.save(todoEntity); + return todoEntityMapper.toDomain(savedTodo); + } + +} + diff --git a/src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/entity/TodoEntity.java b/src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/entity/TodoEntity.java new file mode 100644 index 00000000..66307612 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/entity/TodoEntity.java @@ -0,0 +1,36 @@ +package com.sopromadze.blogapi.infrastructure.persistence.postgresql.entity; + +import com.sopromadze.blogapi.model.metadata.MetadataEntity; +import com.sopromadze.blogapi.model.user.UserEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@Entity +@Table(name = "todos", uniqueConstraints = { @UniqueConstraint(columnNames = { "title" }) }) +public class TodoEntity extends MetadataEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "todo_id") + private Long todoId; + + @Column(name = "title") + private String title; + + @Column(name = "completed") + private boolean completed; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private UserEntity user; + +} diff --git a/src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/mapper/TodoEntityMapper.java b/src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/mapper/TodoEntityMapper.java new file mode 100644 index 00000000..9db5bdc2 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/mapper/TodoEntityMapper.java @@ -0,0 +1,14 @@ +package com.sopromadze.blogapi.infrastructure.persistence.postgresql.mapper; + +import com.sopromadze.blogapi.domain.model.Todo; +import com.sopromadze.blogapi.infrastructure.persistence.postgresql.entity.TodoEntity; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface TodoEntityMapper { + + TodoEntity toEntity(Todo todo); + + Todo toDomain(TodoEntity todoEntity); + +} diff --git a/src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/repository/TodoRepository.java b/src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/repository/TodoRepository.java new file mode 100644 index 00000000..ecb8d3b6 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/infrastructure/persistence/postgresql/repository/TodoRepository.java @@ -0,0 +1,14 @@ +package com.sopromadze.blogapi.infrastructure.persistence.postgresql.repository; + +import com.sopromadze.blogapi.infrastructure.persistence.postgresql.entity.TodoEntity; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TodoRepository extends JpaRepository { + + Page findByCreatedBy(Long userId, Pageable pageable); + +} diff --git a/src/main/java/com/sopromadze/blogapi/infrastructure/rest/controller/TodoController.java b/src/main/java/com/sopromadze/blogapi/infrastructure/rest/controller/TodoController.java new file mode 100644 index 00000000..77933e25 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/infrastructure/rest/controller/TodoController.java @@ -0,0 +1,101 @@ +package com.sopromadze.blogapi.infrastructure.rest.controller; + +import com.sopromadze.blogapi.application.TodoUseCase; +import com.sopromadze.blogapi.domain.model.Todo; +import com.sopromadze.blogapi.infrastructure.persistence.postgresql.entity.TodoEntity; +import com.sopromadze.blogapi.infrastructure.rest.mapper.TodoMapper; +import com.sopromadze.blogapi.infrastructure.rest.payload.request.TodoPostRequestDto; +import com.sopromadze.blogapi.infrastructure.rest.payload.response.TodoResponseDto; +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.utils.AppConstants; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/api/todos") +public class TodoController { + + private final TodoService todoService; + + private final TodoMapper todoMapper; + + private final TodoUseCase todoUseCase; + + @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 TodoPostRequestDto requestDto, + @AuthenticationPrincipal(expression = "username") String username) { + Todo todo = todoMapper.toDomain(requestDto); + Todo createdTodo = todoUseCase.createTodo(todo, username); + TodoResponseDto responseDto = todoMapper.toResponseDto(createdTodo); + + return ResponseEntity.status(HttpStatus.CREATED) + .body(responseDto); + } + + @GetMapping("/{id}") + @PreAuthorize("hasRole('USER')") + public ResponseEntity getTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) { + TodoEntity todoEntity = todoService.getTodo(id, currentUser); + + return new ResponseEntity<>(todoEntity, HttpStatus.OK); + } + + @PutMapping("/{id}") + @PreAuthorize("hasRole('USER')") + public ResponseEntity updateTodo(@PathVariable(value = "id") Long id, @Valid @RequestBody TodoEntity newTodoEntity, + @CurrentUser UserPrincipal currentUser) { + TodoEntity updatedTodoEntity = todoService.updateTodo(id, newTodoEntity, currentUser); + + return new ResponseEntity<>(updatedTodoEntity, 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) { + + TodoEntity todoEntity = todoService.completeTodo(id, currentUser); + + return new ResponseEntity<>(todoEntity, HttpStatus.OK); + } + + @PutMapping("/{id}/unComplete") + @PreAuthorize("hasRole('USER')") + public ResponseEntity unCompleteTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) { + + TodoEntity todoEntity = todoService.unCompleteTodo(id, currentUser); + + return new ResponseEntity<>(todoEntity, HttpStatus.OK); + } + +} diff --git a/src/main/java/com/sopromadze/blogapi/infrastructure/rest/mapper/TodoMapper.java b/src/main/java/com/sopromadze/blogapi/infrastructure/rest/mapper/TodoMapper.java new file mode 100644 index 00000000..0d7443b1 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/infrastructure/rest/mapper/TodoMapper.java @@ -0,0 +1,15 @@ +package com.sopromadze.blogapi.infrastructure.rest.mapper; + +import com.sopromadze.blogapi.domain.model.Todo; +import com.sopromadze.blogapi.infrastructure.rest.payload.request.TodoPostRequestDto; +import com.sopromadze.blogapi.infrastructure.rest.payload.response.TodoResponseDto; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface TodoMapper { + + Todo toDomain(TodoPostRequestDto todoPostRequestDto); + + TodoResponseDto toResponseDto(Todo todo); + +} diff --git a/src/main/java/com/sopromadze/blogapi/infrastructure/rest/payload/request/TodoPostRequestDto.java b/src/main/java/com/sopromadze/blogapi/infrastructure/rest/payload/request/TodoPostRequestDto.java new file mode 100644 index 00000000..6675d60b --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/infrastructure/rest/payload/request/TodoPostRequestDto.java @@ -0,0 +1,20 @@ +package com.sopromadze.blogapi.infrastructure.rest.payload.request; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TodoPostRequestDto { + + @NotEmpty(message = "The title is required.") + @Size(min = 2, max = 100, message = "The length of title must be between 2 and 100 characters.") + private String title; + +} diff --git a/src/main/java/com/sopromadze/blogapi/infrastructure/rest/payload/response/TodoResponseDto.java b/src/main/java/com/sopromadze/blogapi/infrastructure/rest/payload/response/TodoResponseDto.java new file mode 100644 index 00000000..7dee1fe9 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/infrastructure/rest/payload/response/TodoResponseDto.java @@ -0,0 +1,20 @@ +package com.sopromadze.blogapi.infrastructure.rest.payload.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TodoResponseDto { + + private Long todoId; + + private String title; + + private boolean completed; + +} diff --git a/src/main/java/com/sopromadze/blogapi/model/Album.java b/src/main/java/com/sopromadze/blogapi/model/Album.java index dde10393..e35e80d7 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Album.java +++ b/src/main/java/com/sopromadze/blogapi/model/Album.java @@ -1,35 +1,25 @@ 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 com.sopromadze.blogapi.model.metadata.MetadataEntity; +import com.sopromadze.blogapi.model.user.UserEntity; +import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; -import jakarta.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; +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@Entity +@Table(name = "albums", uniqueConstraints = { @UniqueConstraint(columnNames = { "title" }) }) +public class Album extends MetadataEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -41,25 +31,9 @@ public class Album extends UserDateAudit { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") - private User user; + private UserEntity 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 index 78987447..920d2486 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Category.java +++ b/src/main/java/com/sopromadze/blogapi/model/Category.java @@ -2,20 +2,12 @@ import com.fasterxml.jackson.annotation.JsonIdentityInfo; import com.fasterxml.jackson.annotation.ObjectIdGenerators; -import com.sopromadze.blogapi.model.audit.UserDateAudit; +import com.sopromadze.blogapi.model.metadata.MetadataEntity; +import jakarta.persistence.*; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -26,12 +18,12 @@ @NoArgsConstructor @Table(name = "categories") @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") -public class Category extends UserDateAudit { - private static final long serialVersionUID = 1L; +public class Category extends MetadataEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @Column(name = "category_id") + private Long categoryId; @Column(name = "name") private String name; diff --git a/src/main/java/com/sopromadze/blogapi/model/Comment.java b/src/main/java/com/sopromadze/blogapi/model/Comment.java index 5f896a79..a46d8dd3 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Comment.java +++ b/src/main/java/com/sopromadze/blogapi/model/Comment.java @@ -1,31 +1,23 @@ 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 lombok.NoArgsConstructor; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import com.sopromadze.blogapi.model.metadata.MetadataEntity; +import com.sopromadze.blogapi.model.user.UserEntity; +import jakarta.persistence.*; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; @EqualsAndHashCode(callSuper = true) @Entity @Data @NoArgsConstructor @Table(name = "comments") -public class Comment extends UserDateAudit { +public class Comment extends MetadataEntity { + private static final long serialVersionUID = 1L; @Id @@ -54,7 +46,7 @@ public class Comment extends UserDateAudit { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") - private User user; + private UserEntity user; public Comment(@NotBlank @Size(min = 10, message = "Comment body must be minimum 10 characters") String body) { this.body = body; @@ -66,7 +58,8 @@ public Post getPost() { } @JsonIgnore - public User getUser() { + public UserEntity getUser() { return user; } + } diff --git a/src/main/java/com/sopromadze/blogapi/model/Photo.java b/src/main/java/com/sopromadze/blogapi/model/Photo.java index 9f03fb6a..b0eb324f 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Photo.java +++ b/src/main/java/com/sopromadze/blogapi/model/Photo.java @@ -1,29 +1,20 @@ package com.sopromadze.blogapi.model; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.sopromadze.blogapi.model.audit.UserDateAudit; +import com.sopromadze.blogapi.model.metadata.MetadataEntity; +import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; -import jakarta.validation.constraints.NotBlank; - @EqualsAndHashCode(callSuper = true) @Entity @Data @NoArgsConstructor -@Table(name = "photos", uniqueConstraints = {@UniqueConstraint(columnNames = {"title"})}) -public class Photo extends UserDateAudit { +@Table(name = "photos", uniqueConstraints = { @UniqueConstraint(columnNames = { "title" }) }) +public class Photo extends MetadataEntity { + private static final long serialVersionUID = 1L; @Id @@ -57,4 +48,5 @@ public Photo(@NotBlank String title, @NotBlank String url, @NotBlank String thum 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 index 3c0e945e..fda74a33 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Post.java +++ b/src/main/java/com/sopromadze/blogapi/model/Post.java @@ -3,26 +3,12 @@ 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 com.sopromadze.blogapi.model.metadata.MetadataEntity; +import com.sopromadze.blogapi.model.user.UserEntity; +import jakarta.persistence.*; import lombok.Data; import lombok.EqualsAndHashCode; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinTable; -import jakarta.persistence.ManyToMany; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -30,9 +16,10 @@ @EqualsAndHashCode(callSuper = true) @Entity @Data -@Table(name = "posts", uniqueConstraints = {@UniqueConstraint(columnNames = {"title"})}) +@Table(name = "posts", uniqueConstraints = { @UniqueConstraint(columnNames = { "title" }) }) @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") -public class Post extends UserDateAudit { +public class Post extends MetadataEntity { + private static final long serialVersionUID = 1L; @Id @@ -47,7 +34,7 @@ public class Post extends UserDateAudit { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") - private User user; + private UserEntity user; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "category_id") @@ -58,15 +45,16 @@ public class Post extends UserDateAudit { 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")) + @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() { + public UserEntity getUser() { return user; } - public void setUser(User user) { + public void setUser(UserEntity user) { this.user = user; } @@ -93,4 +81,5 @@ public void setTags(List tags) { 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 index d9ba7431..6048d918 100644 --- a/src/main/java/com/sopromadze/blogapi/model/Tag.java +++ b/src/main/java/com/sopromadze/blogapi/model/Tag.java @@ -1,7 +1,7 @@ package com.sopromadze.blogapi.model; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.sopromadze.blogapi.model.audit.UserDateAudit; +import com.sopromadze.blogapi.model.metadata.MetadataEntity; import jakarta.persistence.*; import lombok.Data; import lombok.EqualsAndHashCode; @@ -17,7 +17,7 @@ @NoArgsConstructor @Table(name = "tags") //@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") -public class Tag extends UserDateAudit { +public class Tag extends MetadataEntity { private static final long serialVersionUID = -5298707266367331514L; @@ -30,7 +30,8 @@ public class Tag extends UserDateAudit { @JsonIgnore @ManyToMany(fetch = FetchType.EAGER) - @JoinTable(name = "post_tag", joinColumns = @JoinColumn(name = "tag_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "post_id", referencedColumnName = "id")) + @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) { diff --git a/src/main/java/com/sopromadze/blogapi/model/Todo.java b/src/main/java/com/sopromadze/blogapi/model/Todo.java deleted file mode 100644 index 555a0bea..00000000 --- a/src/main/java/com/sopromadze/blogapi/model/Todo.java +++ /dev/null @@ -1,48 +0,0 @@ -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 jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; -import jakarta.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/audit/DateAudit.java b/src/main/java/com/sopromadze/blogapi/model/audit/DateAudit.java deleted file mode 100644 index 259bdf5d..00000000 --- a/src/main/java/com/sopromadze/blogapi/model/audit/DateAudit.java +++ /dev/null @@ -1,35 +0,0 @@ -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; - -import jakarta.persistence.Column; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.MappedSuperclass; - -import java.io.Serializable; -import java.time.Instant; - -@MappedSuperclass -@Data -@EntityListeners(AuditingEntityListener.class) -@JsonIgnoreProperties( - value = {"createdAt", "updatedAt"}, - allowGetters = true -) -public abstract class DateAudit implements Serializable { - - private static final long serialVersionUID = 1L; - - @CreatedDate - @Column(nullable = false, updatable = false) - private Instant createdAt; - - @LastModifiedDate - @Column(nullable = false) - private Instant 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 deleted file mode 100644 index 14e4446b..00000000 --- a/src/main/java/com/sopromadze/blogapi/model/audit/UserDateAudit.java +++ /dev/null @@ -1,28 +0,0 @@ -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 jakarta.persistence.Column; -import jakarta.persistence.MappedSuperclass; - -@EqualsAndHashCode(callSuper = true) -@MappedSuperclass -@Data -@JsonIgnoreProperties( - value = {"createdBY", "updatedBy"}, - allowGetters = true -) -public abstract class UserDateAudit extends DateAudit { - private static final long serialVersionUID = 1L; - - @CreatedBy - @Column(updatable = false) - private Long createdBy; - - @LastModifiedBy - private Long updatedBy; -} diff --git a/src/main/java/com/sopromadze/blogapi/model/metadata/MetadataEntity.java b/src/main/java/com/sopromadze/blogapi/model/metadata/MetadataEntity.java new file mode 100644 index 00000000..6f9b0b0f --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/model/metadata/MetadataEntity.java @@ -0,0 +1,43 @@ +package com.sopromadze.blogapi.model.metadata; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.io.Serializable; +import java.time.Instant; + +@MappedSuperclass +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EntityListeners(AuditingEntityListener.class) +public class MetadataEntity implements Serializable { + + @CreatedDate + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt; + + @LastModifiedDate + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; + + @CreatedBy + @Column(name = "created_by", updatable = false) + private Long createdBy; + + @LastModifiedBy + @Column(name = "updated_by") + private Long updatedBy; + +} 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 9f45a401..422001aa 100644 --- a/src/main/java/com/sopromadze/blogapi/model/role/Role.java +++ b/src/main/java/com/sopromadze/blogapi/model/role/Role.java @@ -1,23 +1,18 @@ package com.sopromadze.blogapi.model.role; +import jakarta.persistence.*; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.annotations.NaturalId; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Table; +import java.io.Serializable; @Entity @Data @NoArgsConstructor @Table(name = "roles") -public class Role { +public class Role implements Serializable { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -30,4 +25,5 @@ public class Role { 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 f2064b2f..5282fcc8 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/user/Address.java b/src/main/java/com/sopromadze/blogapi/model/user/Address.java index ef028ca9..5ffe1a61 100644 --- a/src/main/java/com/sopromadze/blogapi/model/user/Address.java +++ b/src/main/java/com/sopromadze/blogapi/model/user/Address.java @@ -1,21 +1,21 @@ package com.sopromadze.blogapi.model.user; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.sopromadze.blogapi.model.audit.UserDateAudit; +import com.sopromadze.blogapi.model.metadata.MetadataEntity; import jakarta.persistence.*; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; - -import java.time.Instant; +import lombok.experimental.SuperBuilder; @EqualsAndHashCode(callSuper = true) -@Entity @Data @NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@Entity @Table(name = "address") -public class Address extends UserDateAudit { - private static final long serialVersionUID = 1L; +public class Address extends MetadataEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -38,66 +38,6 @@ public class Address extends UserDateAudit { 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(); - } + private UserEntity user; - @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 df93e8cd..b507a9d6 100644 --- a/src/main/java/com/sopromadze/blogapi/model/user/Company.java +++ b/src/main/java/com/sopromadze/blogapi/model/user/Company.java @@ -1,27 +1,22 @@ package com.sopromadze.blogapi.model.user; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.sopromadze.blogapi.model.audit.UserDateAudit; +import com.sopromadze.blogapi.model.metadata.MetadataEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.OneToOne; -import jakarta.persistence.Table; - -import java.time.Instant; +import lombok.experimental.SuperBuilder; @EqualsAndHashCode(callSuper = true) -@Entity @Data @NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@Entity @Table(name = "company") -public class Company extends UserDateAudit { +public class Company extends MetadataEntity { + private static final long serialVersionUID = 1L; @Id @@ -38,69 +33,6 @@ public class Company extends UserDateAudit { 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(); - } + private UserEntity user; - @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 c7bbfe2f..a80b1292 100644 --- a/src/main/java/com/sopromadze/blogapi/model/user/Geo.java +++ b/src/main/java/com/sopromadze/blogapi/model/user/Geo.java @@ -1,28 +1,22 @@ package com.sopromadze.blogapi.model.user; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.sopromadze.blogapi.model.audit.UserDateAudit; +import com.sopromadze.blogapi.model.metadata.MetadataEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.OneToOne; -import jakarta.persistence.Table; - -import java.time.Instant; +import lombok.experimental.SuperBuilder; @EqualsAndHashCode(callSuper = true) -@Entity @Data @NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@Entity @Table(name = "geo") -public class Geo extends UserDateAudit { - private static final long serialVersionUID = 1L; +public class Geo extends MetadataEntity { @JsonIgnore @Id @@ -38,56 +32,4 @@ public class Geo extends UserDateAudit { @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 deleted file mode 100644 index 9bf7efd9..00000000 --- a/src/main/java/com/sopromadze/blogapi/model/user/User.java +++ /dev/null @@ -1,208 +0,0 @@ -package com.sopromadze.blogapi.model.user; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.sopromadze.blogapi.model.Album; -import com.sopromadze.blogapi.model.Comment; -import com.sopromadze.blogapi.model.Post; -import com.sopromadze.blogapi.model.Todo; -import com.sopromadze.blogapi.model.audit.DateAudit; -import com.sopromadze.blogapi.model.role.Role; -import jakarta.persistence.*; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Size; -import lombok.*; -import org.hibernate.annotations.NaturalId; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; - -import java.io.Serial; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -@EqualsAndHashCode(callSuper = true) -@Entity -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -@Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = {"username"}), - @UniqueConstraint(columnNames = {"email"})}) -public class User extends DateAudit implements UserDetails { - @Serial - private static final long serialVersionUID = 1L; - - @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(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); - } - } - - @Override - public Collection getAuthorities() { - return List.of(); - } - - @Override - public boolean isAccountNonExpired() { - return true; - } - - @Override - public boolean isAccountNonLocked() { - return true; - } - - @Override - public boolean isCredentialsNonExpired() { - return true; - } - - @Override - public boolean isEnabled() { - return true; - } -} diff --git a/src/main/java/com/sopromadze/blogapi/model/user/UserEntity.java b/src/main/java/com/sopromadze/blogapi/model/user/UserEntity.java new file mode 100644 index 00000000..13abc455 --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/model/user/UserEntity.java @@ -0,0 +1,104 @@ +package com.sopromadze.blogapi.model.user; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.sopromadze.blogapi.infrastructure.persistence.postgresql.entity.TodoEntity; +import com.sopromadze.blogapi.model.Album; +import com.sopromadze.blogapi.model.Comment; +import com.sopromadze.blogapi.model.Post; +import com.sopromadze.blogapi.model.metadata.MetadataEntity; +import com.sopromadze.blogapi.model.role.Role; +import jakarta.persistence.*; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.NaturalId; + +import java.util.List; + +@EqualsAndHashCode(callSuper = true) +@Entity +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@Table(name = "users", uniqueConstraints = { + @UniqueConstraint(columnNames = { "username" }), + @UniqueConstraint(columnNames = { "email" }) +}) +public class UserEntity extends MetadataEntity { + + @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; + +} diff --git a/src/main/java/com/sopromadze/blogapi/payload/AlbumResponse.java b/src/main/java/com/sopromadze/blogapi/payload/AlbumResponse.java index ea57f7df..70d2100c 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/AlbumResponse.java +++ b/src/main/java/com/sopromadze/blogapi/payload/AlbumResponse.java @@ -3,7 +3,7 @@ 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 com.sopromadze.blogapi.model.user.UserEntity; import lombok.Data; import lombok.EqualsAndHashCode; @@ -15,25 +15,27 @@ @Data @JsonInclude(Include.NON_NULL) public class AlbumResponse extends UserDateAuditPayload { - private Long id; - private String title; + private Long id; - private User user; + private String title; - private List photo; + private UserEntity userEntity; - public List getPhoto() { + private List photo; - return photo == null ? null : new ArrayList<>(photo); - } + public List getPhoto() { - public void setPhoto(List photo) { + return photo == null ? null : new ArrayList<>(photo); + } + + public void setPhoto(List photo) { + + if (photo == null) { + this.photo = null; + } else { + this.photo = Collections.unmodifiableList(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 4dfa14ab..9332bef8 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/ApiResponse.java +++ b/src/main/java/com/sopromadze/blogapi/payload/ApiResponse.java @@ -10,35 +10,36 @@ @Data @JsonPropertyOrder({ - "success", - "message" + "success", + "message" }) public class ApiResponse implements Serializable { - @JsonIgnore - private static final long serialVersionUID = 7702134516418120340L; + @JsonIgnore + private static final long serialVersionUID = 7702134516418120340L; - @JsonProperty("success") - private Boolean success; + @JsonProperty("success") + private Boolean success; - @JsonProperty("message") - private String message; + @JsonProperty("message") + private String message; - @JsonIgnore - private HttpStatus status; + @JsonIgnore + private HttpStatus status; - public ApiResponse() { + public ApiResponse() { - } + } - public ApiResponse(Boolean success, String message) { - this.success = success; - this.message = message; - } + 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; + } - 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 0854597e..f62fcd30 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/CommentRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/CommentRequest.java @@ -1,13 +1,14 @@ package com.sopromadze.blogapi.payload; -import lombok.Data; - import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; +import lombok.Data; @Data public class CommentRequest { + @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 index 7ec74a5a..d797fb49 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/DateAuditPayload.java +++ b/src/main/java/com/sopromadze/blogapi/payload/DateAuditPayload.java @@ -4,23 +4,24 @@ public abstract class DateAuditPayload { - private Instant createdAt; + private Instant createdAt; - private Instant updatedAt; + private Instant updatedAt; - public Instant getCreatedAt() { - return createdAt; - } + public Instant getCreatedAt() { + return createdAt; + } - public void setCreatedAt(Instant createdAt) { - this.createdAt = createdAt; - } + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } - public Instant getUpdatedAt() { - return updatedAt; - } + public Instant getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(Instant updatedAt) { + this.updatedAt = 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 a243981b..aa83c97d 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/ExceptionResponse.java +++ b/src/main/java/com/sopromadze/blogapi/payload/ExceptionResponse.java @@ -9,30 +9,34 @@ @Data public class ExceptionResponse { - 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); - } - } + + 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 90df3bcf..43af683a 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/InfoRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/InfoRequest.java @@ -1,8 +1,7 @@ package com.sopromadze.blogapi.payload; -import lombok.Data; - import jakarta.validation.constraints.NotBlank; +import lombok.Data; @Data public class InfoRequest { @@ -32,4 +31,5 @@ public class InfoRequest { private String lat; 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 148aa475..136ab005 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/JwtAuthenticationResponse.java +++ b/src/main/java/com/sopromadze/blogapi/payload/JwtAuthenticationResponse.java @@ -4,11 +4,13 @@ @Data public class JwtAuthenticationResponse { - private String accessToken; - private String tokenType = "Bearer"; - public JwtAuthenticationResponse(String accessToken) { - this.accessToken = accessToken; - } + private String accessToken; + + private String tokenType = "Bearer"; + + public JwtAuthenticationResponse(String accessToken) { + this.accessToken = accessToken; + } } diff --git a/src/main/java/com/sopromadze/blogapi/payload/LoginRequest.java b/src/main/java/com/sopromadze/blogapi/payload/LoginRequest.java index 617b0572..0d3ee7ac 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/LoginRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/LoginRequest.java @@ -1,14 +1,15 @@ package com.sopromadze.blogapi.payload; -import lombok.Data; - import jakarta.validation.constraints.NotBlank; +import lombok.Data; @Data public class LoginRequest { + @NotBlank private String usernameOrEmail; @NotBlank private String password; + } diff --git a/src/main/java/com/sopromadze/blogapi/payload/LoginResponse.java b/src/main/java/com/sopromadze/blogapi/payload/LoginResponse.java index 9b4027f9..5b651c66 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/LoginResponse.java +++ b/src/main/java/com/sopromadze/blogapi/payload/LoginResponse.java @@ -10,7 +10,9 @@ @NoArgsConstructor @Builder public class LoginResponse { + private String token; private long expiresIn; + } diff --git a/src/main/java/com/sopromadze/blogapi/payload/LoginUserDto.java b/src/main/java/com/sopromadze/blogapi/payload/LoginUserDto.java index ca62b79b..aecd836d 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/LoginUserDto.java +++ b/src/main/java/com/sopromadze/blogapi/payload/LoginUserDto.java @@ -10,6 +10,9 @@ @NoArgsConstructor @Builder public class LoginUserDto { + private String email; + 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 9e73e40e..cf600357 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/PagedResponse.java +++ b/src/main/java/com/sopromadze/blogapi/payload/PagedResponse.java @@ -8,41 +8,46 @@ @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) { - 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; - } + + 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 dbfda2ba..ed6a772d 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/PhotoRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/PhotoRequest.java @@ -1,10 +1,9 @@ package com.sopromadze.blogapi.payload; -import lombok.Data; - import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import lombok.Data; @Data public class PhotoRequest { @@ -23,4 +22,5 @@ public class PhotoRequest { @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 e695b977..a0cfb056 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/PhotoResponse.java +++ b/src/main/java/com/sopromadze/blogapi/payload/PhotoResponse.java @@ -4,18 +4,23 @@ @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; - } + + 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; + } } diff --git a/src/main/java/com/sopromadze/blogapi/payload/PostRequest.java b/src/main/java/com/sopromadze/blogapi/payload/PostRequest.java index 86742bf3..921f6535 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/PostRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/PostRequest.java @@ -1,10 +1,9 @@ package com.sopromadze.blogapi.payload; -import lombok.Data; - import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import lombok.Data; import java.util.ArrayList; import java.util.Collections; @@ -39,4 +38,5 @@ public void setTags(List tags) { 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 f054e518..585bb0f8 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/PostResponse.java +++ b/src/main/java/com/sopromadze/blogapi/payload/PostResponse.java @@ -8,24 +8,27 @@ @Data public class PostResponse { - private String title; - private String body; - private String category; - private List tags; + private String title; + private String body; - public List getTags() { + private String category; - return tags == null ? null : new ArrayList<>(tags); - } + private List tags; - public void setTags(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); + } + } - if (tags == null) { - this.tags = null; - } else { - this.tags = Collections.unmodifiableList(tags); - } - } } diff --git a/src/main/java/com/sopromadze/blogapi/payload/RegisterUserDto.java b/src/main/java/com/sopromadze/blogapi/payload/RegisterUserDto.java index 4b31726c..b52e43db 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/RegisterUserDto.java +++ b/src/main/java/com/sopromadze/blogapi/payload/RegisterUserDto.java @@ -10,8 +10,13 @@ @NoArgsConstructor @Builder public class RegisterUserDto { + private String email; + private String password; + private String firstName; + private String lastName; + } diff --git a/src/main/java/com/sopromadze/blogapi/payload/SignUpRequest.java b/src/main/java/com/sopromadze/blogapi/payload/SignUpRequest.java index 2fd4b2fa..6fda7087 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/SignUpRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/SignUpRequest.java @@ -1,13 +1,13 @@ package com.sopromadze.blogapi.payload; -import lombok.Data; - import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; +import lombok.Data; @Data public class SignUpRequest { + @NotBlank @Size(min = 4, max = 40) private String firstName; @@ -28,4 +28,5 @@ public class SignUpRequest { @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 index 834f095d..8d9fdc71 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/UserDateAuditPayload.java +++ b/src/main/java/com/sopromadze/blogapi/payload/UserDateAuditPayload.java @@ -6,8 +6,9 @@ @EqualsAndHashCode(callSuper = true) @Data public abstract class UserDateAuditPayload extends DateAuditPayload { - private Long createdBy; - private Long updatedBy; + 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 c5d09fc2..60267fd6 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/UserIdentityAvailability.java +++ b/src/main/java/com/sopromadze/blogapi/payload/UserIdentityAvailability.java @@ -6,6 +6,7 @@ @Data @AllArgsConstructor public class UserIdentityAvailability { - private Boolean available; + + private Boolean available; } diff --git a/src/main/java/com/sopromadze/blogapi/payload/UserProfile.java b/src/main/java/com/sopromadze/blogapi/payload/UserProfile.java index b428ea12..031421fe 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/UserProfile.java +++ b/src/main/java/com/sopromadze/blogapi/payload/UserProfile.java @@ -12,15 +12,27 @@ @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; + + 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 7fe8fe92..77fd2e42 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/UserSummary.java +++ b/src/main/java/com/sopromadze/blogapi/payload/UserSummary.java @@ -6,8 +6,13 @@ @Data @AllArgsConstructor public class UserSummary { - private Long id; - private String username; - private String firstName; - private String lastName; + + 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 index 83e4f1eb..592d4dd6 100644 --- a/src/main/java/com/sopromadze/blogapi/payload/request/AlbumRequest.java +++ b/src/main/java/com/sopromadze/blogapi/payload/request/AlbumRequest.java @@ -1,7 +1,7 @@ package com.sopromadze.blogapi.payload.request; import com.sopromadze.blogapi.model.Photo; -import com.sopromadze.blogapi.model.user.User; +import com.sopromadze.blogapi.model.user.UserEntity; import com.sopromadze.blogapi.payload.UserDateAuditPayload; import lombok.Data; @@ -12,25 +12,26 @@ @Data public class AlbumRequest extends UserDateAuditPayload { - private Long id; + private Long id; - private String title; + private String title; - private User user; + private UserEntity userEntity; - private List photo; + private List photo; - public List getPhoto() { + public List getPhoto() { - return photo == null ? null : new ArrayList<>(photo); - } + return photo == null ? null : new ArrayList<>(photo); + } - public void setPhoto(List photo) { + public void setPhoto(List photo) { + + if (photo == null) { + this.photo = null; + } else { + this.photo = Collections.unmodifiableList(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 12d6837f..3cda3a4c 100644 --- a/src/main/java/com/sopromadze/blogapi/repository/AlbumRepository.java +++ b/src/main/java/com/sopromadze/blogapi/repository/AlbumRepository.java @@ -8,5 +8,7 @@ @Repository public interface AlbumRepository extends JpaRepository { - Page findByCreatedBy(Long userId, Pageable pageable); + + Page findByCreatedBy(Long userId, Pageable pageable); + } diff --git a/src/main/java/com/sopromadze/blogapi/repository/CommentRepository.java b/src/main/java/com/sopromadze/blogapi/repository/CommentRepository.java index b4fb3be3..cebde2e4 100644 --- a/src/main/java/com/sopromadze/blogapi/repository/CommentRepository.java +++ b/src/main/java/com/sopromadze/blogapi/repository/CommentRepository.java @@ -8,5 +8,7 @@ @Repository public interface CommentRepository extends JpaRepository { - Page findByPostId(Long postId, Pageable pageable); + + 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 a6f52698..e76bab15 100644 --- a/src/main/java/com/sopromadze/blogapi/repository/PhotoRepository.java +++ b/src/main/java/com/sopromadze/blogapi/repository/PhotoRepository.java @@ -8,5 +8,7 @@ @Repository public interface PhotoRepository extends JpaRepository { - Page findByAlbumId(Long albumId, Pageable pageable); + + 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 a748afb8..3b269c23 100644 --- a/src/main/java/com/sopromadze/blogapi/repository/PostRepository.java +++ b/src/main/java/com/sopromadze/blogapi/repository/PostRepository.java @@ -11,11 +11,13 @@ @Repository public interface PostRepository extends JpaRepository { - Page findByCreatedBy(Long userId, Pageable pageable); - Page findByCategory(Long categoryId, Pageable pageable); + Page findByCreatedBy(Long userId, Pageable pageable); - Page findByTags(List tags, Pageable pageable); + Page findByCategory(Long categoryId, Pageable pageable); + + Page findByTags(List tags, Pageable pageable); + + Long countByCreatedBy(Long userId); - 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 067294c6..b9eac3c7 100644 --- a/src/main/java/com/sopromadze/blogapi/repository/RoleRepository.java +++ b/src/main/java/com/sopromadze/blogapi/repository/RoleRepository.java @@ -7,5 +7,7 @@ import java.util.Optional; public interface RoleRepository extends JpaRepository { - Optional findByName(RoleName name); + + 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 index e9620636..1ec4e3d2 100644 --- a/src/main/java/com/sopromadze/blogapi/repository/TagRepository.java +++ b/src/main/java/com/sopromadze/blogapi/repository/TagRepository.java @@ -6,5 +6,7 @@ @Repository public interface TagRepository extends JpaRepository { - Tag findByName(String name); + + 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 deleted file mode 100644 index 3cb55e71..00000000 --- a/src/main/java/com/sopromadze/blogapi/repository/TodoRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sopromadze.blogapi.repository; - -import com.sopromadze.blogapi.model.Todo; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -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 9fd36ea2..5b35bd10 100644 --- a/src/main/java/com/sopromadze/blogapi/repository/UserRepository.java +++ b/src/main/java/com/sopromadze/blogapi/repository/UserRepository.java @@ -1,33 +1,34 @@ package com.sopromadze.blogapi.repository; import com.sopromadze.blogapi.exception.ResourceNotFoundException; -import com.sopromadze.blogapi.model.user.User; +import com.sopromadze.blogapi.model.user.UserEntity; import com.sopromadze.blogapi.security.UserPrincipal; +import jakarta.validation.constraints.NotBlank; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import jakarta.validation.constraints.NotBlank; - import java.util.Optional; @Repository -public interface UserRepository extends JpaRepository { - Optional findByUsername(@NotBlank String username); +public interface UserRepository extends JpaRepository { - Optional findByEmail(@NotBlank String email); + 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); + Optional findByUsernameOrEmail(String username, String email); - default User getUser(UserPrincipal currentUser) { + default UserEntity getUser(UserPrincipal currentUser) { return getUserByName(currentUser.getUsername()); } - default User getUserByName(String username) { + default UserEntity 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 400b4a48..2e5b1b27 100644 --- a/src/main/java/com/sopromadze/blogapi/security/CurrentUser.java +++ b/src/main/java/com/sopromadze/blogapi/security/CurrentUser.java @@ -2,15 +2,12 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; -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; +import java.lang.annotation.*; -@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE }) +@Target({ ElementType.PARAMETER, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented @AuthenticationPrincipal public @interface CurrentUser { + } diff --git a/src/main/java/com/sopromadze/blogapi/security/UserPrincipal.java b/src/main/java/com/sopromadze/blogapi/security/UserPrincipal.java index 3402459f..eb065eb5 100644 --- a/src/main/java/com/sopromadze/blogapi/security/UserPrincipal.java +++ b/src/main/java/com/sopromadze/blogapi/security/UserPrincipal.java @@ -1,121 +1,71 @@ package com.sopromadze.blogapi.security; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.sopromadze.blogapi.model.user.User; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; import org.springframework.security.core.GrantedAuthority; -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; +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder public class UserPrincipal implements UserDetails { - 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 authorities; - - public UserPrincipal(Long id, String firstName, String lastName, String username, String email, String password, - Collection 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 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; - } + + private Long id; + + private String firstName; + + private String lastName; + + private String username; + + @JsonIgnore + private String email; + + @JsonIgnore + private String password; + + private Collection authorities; + + @Override + public Collection 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; + } + } diff --git a/src/main/java/com/sopromadze/blogapi/service/AlbumService.java b/src/main/java/com/sopromadze/blogapi/service/AlbumService.java index ef4a3621..1ce6dc26 100644 --- a/src/main/java/com/sopromadze/blogapi/service/AlbumService.java +++ b/src/main/java/com/sopromadze/blogapi/service/AlbumService.java @@ -10,16 +10,16 @@ public interface AlbumService { - PagedResponse getAllAlbums(int page, int size); + PagedResponse getAllAlbums(int page, int size); - ResponseEntity addAlbum(AlbumRequest albumRequest, UserPrincipal currentUser); + ResponseEntity addAlbum(AlbumRequest albumRequest, UserPrincipal currentUser); - ResponseEntity getAlbum(Long id); + ResponseEntity getAlbum(Long id); - ResponseEntity updateAlbum(Long id, AlbumRequest newAlbum, UserPrincipal currentUser); + ResponseEntity updateAlbum(Long id, AlbumRequest newAlbum, UserPrincipal currentUser); - ResponseEntity deleteAlbum(Long id, UserPrincipal currentUser); + ResponseEntity deleteAlbum(Long id, UserPrincipal currentUser); - PagedResponse getUserAlbums(String username, int page, int size); + PagedResponse getUserAlbums(String username, int page, int size); } diff --git a/src/main/java/com/sopromadze/blogapi/service/AuthenticationService.java b/src/main/java/com/sopromadze/blogapi/service/AuthenticationService.java index 5145e8d9..2f130475 100644 --- a/src/main/java/com/sopromadze/blogapi/service/AuthenticationService.java +++ b/src/main/java/com/sopromadze/blogapi/service/AuthenticationService.java @@ -1,8 +1,11 @@ package com.sopromadze.blogapi.service; -import com.sopromadze.blogapi.model.user.User; +import com.sopromadze.blogapi.model.role.Role; +import com.sopromadze.blogapi.model.role.RoleName; +import com.sopromadze.blogapi.model.user.UserEntity; import com.sopromadze.blogapi.payload.LoginUserDto; import com.sopromadze.blogapi.payload.RegisterUserDto; +import com.sopromadze.blogapi.repository.RoleRepository; import com.sopromadze.blogapi.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.AuthenticationManager; @@ -10,27 +13,49 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + @RequiredArgsConstructor @Service public class AuthenticationService { + private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; private final AuthenticationManager authenticationManager; - public User signup(RegisterUserDto input) { - User user = User.builder() + private final RoleRepository roleRepository; + + public UserEntity signup(RegisterUserDto input) { + + List roleNames = new ArrayList<>(List.of(RoleName.ROLE_USER)); + + if (userRepository.count() == 0) { + roleNames.add(RoleName.ROLE_ADMIN); + } + + List roles = roleNames.stream() + .map(roleRepository::findByName) + .filter(Optional::isPresent) + .map(Optional::get) + .toList(); + + UserEntity userEntity = UserEntity.builder() .firstName(input.getFirstName()) .lastName(input.getLastName()) .email(input.getEmail()) .username(input.getEmail()) + .roles(roles) .password(passwordEncoder.encode(input.getPassword())) .build(); - return userRepository.save(user); + return userRepository.save(userEntity); + } - public User authenticate(LoginUserDto input) { + public UserEntity authenticate(LoginUserDto input) { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( input.getEmail(), @@ -41,4 +66,5 @@ public User authenticate(LoginUserDto input) { return userRepository.findByEmail(input.getEmail()) .orElseThrow(); } + } diff --git a/src/main/java/com/sopromadze/blogapi/service/CategoryService.java b/src/main/java/com/sopromadze/blogapi/service/CategoryService.java index 65734a3e..437ef45b 100644 --- a/src/main/java/com/sopromadze/blogapi/service/CategoryService.java +++ b/src/main/java/com/sopromadze/blogapi/service/CategoryService.java @@ -9,15 +9,15 @@ public interface CategoryService { - PagedResponse getAllCategories(int page, int size); + PagedResponse getAllCategories(int page, int size); - ResponseEntity getCategory(Long id); + ResponseEntity getCategory(Long id); - ResponseEntity addCategory(Category category, UserPrincipal currentUser); + ResponseEntity addCategory(Category category, UserPrincipal currentUser); - ResponseEntity updateCategory(Long id, Category newCategory, UserPrincipal currentUser) - throws UnauthorizedException; + ResponseEntity updateCategory(Long id, Category newCategory, UserPrincipal currentUser) + throws UnauthorizedException; - ResponseEntity deleteCategory(Long id, 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 cdbc34de..164ab957 100644 --- a/src/main/java/com/sopromadze/blogapi/service/CommentService.java +++ b/src/main/java/com/sopromadze/blogapi/service/CommentService.java @@ -8,14 +8,14 @@ public interface CommentService { - PagedResponse getAllComments(Long postId, int page, int size); + PagedResponse getAllComments(Long postId, int page, int size); - Comment addComment(CommentRequest commentRequest, Long postId, UserPrincipal currentUser); + Comment addComment(CommentRequest commentRequest, Long postId, UserPrincipal currentUser); - Comment getComment(Long postId, Long id); + Comment getComment(Long postId, Long id); - Comment updateComment(Long postId, Long id, CommentRequest commentRequest, UserPrincipal currentUser); + Comment updateComment(Long postId, Long id, CommentRequest commentRequest, UserPrincipal currentUser); - ApiResponse deleteComment(Long postId, Long id, UserPrincipal currentUser); + ApiResponse deleteComment(Long postId, Long id, UserPrincipal currentUser); } diff --git a/src/main/java/com/sopromadze/blogapi/service/CustomUserDetailsService.java b/src/main/java/com/sopromadze/blogapi/service/CustomUserDetailsService.java deleted file mode 100644 index 55084feb..00000000 --- a/src/main/java/com/sopromadze/blogapi/service/CustomUserDetailsService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sopromadze.blogapi.service; - -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UsernameNotFoundException; - -public interface CustomUserDetailsService { - - UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException; - - UserDetails loadUserById(Long id); - -} \ No newline at end of file diff --git a/src/main/java/com/sopromadze/blogapi/service/JwtService.java b/src/main/java/com/sopromadze/blogapi/service/JwtService.java index 55f33ebc..acba5c26 100644 --- a/src/main/java/com/sopromadze/blogapi/service/JwtService.java +++ b/src/main/java/com/sopromadze/blogapi/service/JwtService.java @@ -2,21 +2,23 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.io.Decoders; -import io.jsonwebtoken.security.Keys; +import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; -import java.security.Key; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @Service +@Data public class JwtService { + @Value("${security.jwt.secret-key}") private String secretKey; @@ -32,26 +34,22 @@ public T extractClaim(String token, Function claimsResolver) { return claimsResolver.apply(claims); } - public String generateToken(UserDetails userDetails) { - return generateToken(new HashMap<>(), userDetails); - } - - public String generateToken(Map extraClaims, UserDetails userDetails) { - return buildToken(extraClaims, userDetails, jwtExpiration); + public String generateToken(String username) { + return generateToken(new HashMap<>(), username); } - public long getExpirationTime() { - return jwtExpiration; + public String generateToken(Map extraClaims, String username) { + return buildToken(extraClaims, username, jwtExpiration); } - private String buildToken(Map extraClaims, UserDetails userDetails, long expiration) { + private String buildToken(Map extraClaims, String username, long expiration) { return Jwts .builder() - .setClaims(extraClaims) - .setSubject(userDetails.getUsername()) - .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + expiration)) - .signWith(getSignInKey(), SignatureAlgorithm.HS256) + .claims(extraClaims) + .subject(username) + .issuedAt(new Date(System.currentTimeMillis())) + .expiration(new Date(System.currentTimeMillis() + expiration)) + .signWith(getSignInKey()) .compact(); } @@ -70,15 +68,16 @@ private Date extractExpiration(String token) { private Claims extractAllClaims(String token) { return Jwts - .parserBuilder() - .setSigningKey(getSignInKey()) + .parser() + .verifyWith(getSignInKey()) .build() - .parseClaimsJws(token) - .getBody(); + .parseSignedClaims(token) + .getPayload(); } - private Key getSignInKey() { + private SecretKey getSignInKey() { byte[] keyBytes = Decoders.BASE64.decode(secretKey); - return Keys.hmacShaKeyFor(keyBytes); + return new SecretKeySpec(keyBytes, "HmacSHA256"); } + } \ 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 b59c767f..ba954f2d 100644 --- a/src/main/java/com/sopromadze/blogapi/service/PhotoService.java +++ b/src/main/java/com/sopromadze/blogapi/service/PhotoService.java @@ -8,16 +8,16 @@ public interface PhotoService { - PagedResponse getAllPhotos(int page, int size); + PagedResponse getAllPhotos(int page, int size); - PhotoResponse getPhoto(Long id); + PhotoResponse getPhoto(Long id); - PhotoResponse updatePhoto(Long id, PhotoRequest photoRequest, UserPrincipal currentUser); + PhotoResponse updatePhoto(Long id, PhotoRequest photoRequest, UserPrincipal currentUser); - PhotoResponse addPhoto(PhotoRequest photoRequest, UserPrincipal currentUser); + PhotoResponse addPhoto(PhotoRequest photoRequest, UserPrincipal currentUser); - ApiResponse deletePhoto(Long id, UserPrincipal currentUser); + ApiResponse deletePhoto(Long id, UserPrincipal currentUser); - PagedResponse getAllPhotosByAlbum(Long albumId, int page, int size); + PagedResponse getAllPhotosByAlbum(Long albumId, int page, int 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 d4754205..e2b4fccf 100644 --- a/src/main/java/com/sopromadze/blogapi/service/PostService.java +++ b/src/main/java/com/sopromadze/blogapi/service/PostService.java @@ -9,20 +9,20 @@ public interface PostService { - PagedResponse getAllPosts(int page, int size); + PagedResponse getAllPosts(int page, int size); - PagedResponse getPostsByCreatedBy(String username, int page, int size); + PagedResponse getPostsByCreatedBy(String username, int page, int size); - PagedResponse getPostsByCategory(Long id, int page, int size); + PagedResponse getPostsByCategory(Long id, int page, int size); - PagedResponse getPostsByTag(Long id, int page, int size); + PagedResponse getPostsByTag(Long id, int page, int size); - Post updatePost(Long id, PostRequest newPostRequest, UserPrincipal currentUser); + Post updatePost(Long id, PostRequest newPostRequest, UserPrincipal currentUser); - ApiResponse deletePost(Long id, UserPrincipal currentUser); + ApiResponse deletePost(Long id, UserPrincipal currentUser); - PostResponse addPost(PostRequest postRequest, UserPrincipal currentUser); + PostResponse addPost(PostRequest postRequest, UserPrincipal currentUser); - Post getPost(Long id); + Post getPost(Long id); } diff --git a/src/main/java/com/sopromadze/blogapi/service/TagService.java b/src/main/java/com/sopromadze/blogapi/service/TagService.java index db321cca..07c8c824 100644 --- a/src/main/java/com/sopromadze/blogapi/service/TagService.java +++ b/src/main/java/com/sopromadze/blogapi/service/TagService.java @@ -7,14 +7,14 @@ public interface TagService { - PagedResponse getAllTags(int page, int size); + PagedResponse getAllTags(int page, int size); - Tag getTag(Long id); + Tag getTag(Long id); - Tag addTag(Tag tag, UserPrincipal currentUser); + Tag addTag(Tag tag, UserPrincipal currentUser); - Tag updateTag(Long id, Tag newTag, UserPrincipal currentUser); + Tag updateTag(Long id, Tag newTag, UserPrincipal currentUser); - ApiResponse deleteTag(Long id, 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 23e7903d..4c06cf05 100644 --- a/src/main/java/com/sopromadze/blogapi/service/TodoService.java +++ b/src/main/java/com/sopromadze/blogapi/service/TodoService.java @@ -1,24 +1,24 @@ package com.sopromadze.blogapi.service; -import com.sopromadze.blogapi.model.Todo; +import com.sopromadze.blogapi.infrastructure.persistence.postgresql.entity.TodoEntity; import com.sopromadze.blogapi.payload.ApiResponse; import com.sopromadze.blogapi.payload.PagedResponse; import com.sopromadze.blogapi.security.UserPrincipal; public interface TodoService { - Todo completeTodo(Long id, UserPrincipal currentUser); + TodoEntity completeTodo(Long id, UserPrincipal currentUser); - Todo unCompleteTodo(Long id, UserPrincipal currentUser); + TodoEntity unCompleteTodo(Long id, UserPrincipal currentUser); - PagedResponse getAllTodos(UserPrincipal currentUser, int page, int size); + PagedResponse getAllTodos(UserPrincipal currentUser, int page, int size); - Todo addTodo(Todo todo, UserPrincipal currentUser); + TodoEntity addTodo(TodoEntity todoEntity, String username); - Todo getTodo(Long id, UserPrincipal currentUser); + TodoEntity getTodo(Long id, UserPrincipal currentUser); - Todo updateTodo(Long id, Todo newTodo, UserPrincipal currentUser); + TodoEntity updateTodo(Long id, TodoEntity newTodoEntity, UserPrincipal currentUser); - ApiResponse deleteTodo(Long id, UserPrincipal currentUser); + ApiResponse deleteTodo(Long id, UserPrincipal currentUser); } diff --git a/src/main/java/com/sopromadze/blogapi/service/UserService.java b/src/main/java/com/sopromadze/blogapi/service/UserService.java index a134a637..b213d3a6 100644 --- a/src/main/java/com/sopromadze/blogapi/service/UserService.java +++ b/src/main/java/com/sopromadze/blogapi/service/UserService.java @@ -1,33 +1,29 @@ package com.sopromadze.blogapi.service; -import com.sopromadze.blogapi.model.user.User; -import com.sopromadze.blogapi.payload.ApiResponse; -import com.sopromadze.blogapi.payload.InfoRequest; -import com.sopromadze.blogapi.payload.UserIdentityAvailability; -import com.sopromadze.blogapi.payload.UserProfile; -import com.sopromadze.blogapi.payload.UserSummary; +import com.sopromadze.blogapi.model.user.UserEntity; +import com.sopromadze.blogapi.payload.*; import com.sopromadze.blogapi.security.UserPrincipal; public interface UserService { - UserSummary getCurrentUser(UserPrincipal currentUser); + UserSummary getCurrentUser(UserPrincipal currentUser); - UserIdentityAvailability checkUsernameAvailability(String username); + UserIdentityAvailability checkUsernameAvailability(String username); - UserIdentityAvailability checkEmailAvailability(String email); + UserIdentityAvailability checkEmailAvailability(String email); - UserProfile getUserProfile(String username); + UserProfile getUserProfile(String username); - User addUser(User user); + UserEntity addUser(UserEntity userEntity); - User updateUser(User newUser, String username, UserPrincipal currentUser); + UserEntity updateUser(UserEntity newUserEntity, String username, UserPrincipal currentUser); - ApiResponse deleteUser(String username, UserPrincipal currentUser); + ApiResponse deleteUser(String username, UserPrincipal currentUser); - ApiResponse giveAdmin(String username); + ApiResponse giveAdmin(String username); - ApiResponse removeAdmin(String username); + ApiResponse removeAdmin(String username); - UserProfile setOrUpdateInfo(UserPrincipal currentUser, InfoRequest infoRequest); + UserProfile setOrUpdateInfo(UserPrincipal currentUser, InfoRequest infoRequest); } \ No newline at end of file diff --git a/src/main/java/com/sopromadze/blogapi/service/impl/AlbumServiceImpl.java b/src/main/java/com/sopromadze/blogapi/service/impl/AlbumServiceImpl.java index 6964008e..16fabf2b 100644 --- a/src/main/java/com/sopromadze/blogapi/service/impl/AlbumServiceImpl.java +++ b/src/main/java/com/sopromadze/blogapi/service/impl/AlbumServiceImpl.java @@ -4,7 +4,7 @@ import com.sopromadze.blogapi.exception.ResourceNotFoundException; import com.sopromadze.blogapi.model.Album; import com.sopromadze.blogapi.model.role.RoleName; -import com.sopromadze.blogapi.model.user.User; +import com.sopromadze.blogapi.model.user.UserEntity; import com.sopromadze.blogapi.payload.AlbumResponse; import com.sopromadze.blogapi.payload.ApiResponse; import com.sopromadze.blogapi.payload.PagedResponse; @@ -33,101 +33,104 @@ @Service public class AlbumServiceImpl implements AlbumService { - private static final String CREATED_AT = "createdAt"; - private static final String ALBUM_STR = "Album"; + private static final String CREATED_AT = "createdAt"; - private static final String YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION = "You don't have permission to make this operation"; + private static final String ALBUM_STR = "Album"; - @Autowired - private AlbumRepository albumRepository; + private static final String YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION = "You don't have permission to make this operation"; - @Autowired - private UserRepository userRepository; + @Autowired + private AlbumRepository albumRepository; - @Autowired - private ModelMapper modelMapper; + @Autowired + private UserRepository userRepository; - @Override - public PagedResponse getAllAlbums(int page, int size) { - AppUtils.validatePageNumberAndSize(page, size); + @Autowired + private ModelMapper modelMapper; - Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); + @Override + public PagedResponse getAllAlbums(int page, int size) { + AppUtils.validatePageNumberAndSize(page, size); - Page albums = albumRepository.findAll(pageable); + Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); - if (albums.getNumberOfElements() == 0) { - return new PagedResponse<>(Collections.emptyList(), albums.getNumber(), albums.getSize(), albums.getTotalElements(), - albums.getTotalPages(), albums.isLast()); - } + Page albums = albumRepository.findAll(pageable); - List albumResponses = Arrays.asList(modelMapper.map(albums.getContent(), AlbumResponse[].class)); + if (albums.getNumberOfElements() == 0) { + return new PagedResponse<>(Collections.emptyList(), albums.getNumber(), albums.getSize(), albums.getTotalElements(), + albums.getTotalPages(), albums.isLast()); + } - return new PagedResponse<>(albumResponses, albums.getNumber(), albums.getSize(), albums.getTotalElements(), albums.getTotalPages(), - albums.isLast()); - } + List albumResponses = Arrays.asList(modelMapper.map(albums.getContent(), AlbumResponse[].class)); - @Override - public ResponseEntity addAlbum(AlbumRequest albumRequest, UserPrincipal currentUser) { - User user = userRepository.getUser(currentUser); + return new PagedResponse<>(albumResponses, albums.getNumber(), albums.getSize(), albums.getTotalElements(), albums.getTotalPages(), + albums.isLast()); + } - Album album = new Album(); + @Override + public ResponseEntity addAlbum(AlbumRequest albumRequest, UserPrincipal currentUser) { + UserEntity userEntity = userRepository.getUser(currentUser); - modelMapper.map(albumRequest, album); + Album album = new Album(); - album.setUser(user); - Album newAlbum = albumRepository.save(album); - return new ResponseEntity<>(newAlbum, HttpStatus.CREATED); - } + modelMapper.map(albumRequest, album); - @Override - public ResponseEntity getAlbum(Long id) { - Album album = albumRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(ALBUM_STR, ID, id)); - return new ResponseEntity<>(album, HttpStatus.OK); - } + album.setUser(userEntity); + Album newAlbum = albumRepository.save(album); + return new ResponseEntity<>(newAlbum, HttpStatus.CREATED); + } - @Override - public ResponseEntity updateAlbum(Long id, AlbumRequest newAlbum, UserPrincipal currentUser) { - Album album = albumRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(ALBUM_STR, ID, id)); - User user = userRepository.getUser(currentUser); - 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); + @Override + public ResponseEntity getAlbum(Long id) { + Album album = albumRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(ALBUM_STR, ID, id)); + return new ResponseEntity<>(album, HttpStatus.OK); + } - AlbumResponse albumResponse = new AlbumResponse(); + @Override + public ResponseEntity updateAlbum(Long id, AlbumRequest newAlbum, UserPrincipal currentUser) { + Album album = albumRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(ALBUM_STR, ID, id)); + UserEntity userEntity = userRepository.getUser(currentUser); + if (album.getUser().getId().equals(userEntity.getId()) || currentUser.getAuthorities() + .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { + album.setTitle(newAlbum.getTitle()); + Album updatedAlbum = albumRepository.save(album); - modelMapper.map(updatedAlbum, albumResponse); + AlbumResponse albumResponse = new AlbumResponse(); - return new ResponseEntity<>(albumResponse, HttpStatus.OK); - } + modelMapper.map(updatedAlbum, albumResponse); - throw new BlogapiException(HttpStatus.UNAUTHORIZED, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); - } + return new ResponseEntity<>(albumResponse, HttpStatus.OK); + } - @Override - public ResponseEntity deleteAlbum(Long id, UserPrincipal currentUser) { - Album album = albumRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(ALBUM_STR, ID, id)); - User user = userRepository.getUser(currentUser); - if (album.getUser().getId().equals(user.getId()) || currentUser.getAuthorities() - .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { - albumRepository.deleteById(id); - return new ResponseEntity<>(new ApiResponse(Boolean.TRUE, "You successfully deleted album"), HttpStatus.OK); - } + throw new BlogapiException(HttpStatus.UNAUTHORIZED, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); + } - throw new BlogapiException(HttpStatus.UNAUTHORIZED, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); - } + @Override + public ResponseEntity deleteAlbum(Long id, UserPrincipal currentUser) { + Album album = albumRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(ALBUM_STR, ID, id)); + UserEntity userEntity = userRepository.getUser(currentUser); + if (album.getUser().getId().equals(userEntity.getId()) || currentUser.getAuthorities() + .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { + albumRepository.deleteById(id); + return new ResponseEntity<>(new ApiResponse(Boolean.TRUE, "You successfully deleted album"), HttpStatus.OK); + } - @Override - public PagedResponse getUserAlbums(String username, int page, int size) { - User user = userRepository.getUserByName(username); + throw new BlogapiException(HttpStatus.UNAUTHORIZED, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); + } - Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); + @Override + public PagedResponse getUserAlbums(String username, int page, int size) { + UserEntity userEntity = userRepository.getUserByName(username); - Page albums = albumRepository.findByCreatedBy(user.getId(), pageable); + Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); - List content = albums.getNumberOfElements() > 0 ? albums.getContent() : Collections.emptyList(); + Page albums = albumRepository.findByCreatedBy(userEntity.getId(), pageable); + + List content = albums.getNumberOfElements() > 0 ? albums.getContent() : Collections.emptyList(); + + return new PagedResponse<>(content, albums.getNumber(), albums.getSize(), albums.getTotalElements(), albums.getTotalPages(), + albums.isLast()); + } - return new PagedResponse<>(content, albums.getNumber(), albums.getSize(), albums.getTotalElements(), albums.getTotalPages(), albums.isLast()); - } } diff --git a/src/main/java/com/sopromadze/blogapi/service/impl/CategoryServiceImpl.java b/src/main/java/com/sopromadze/blogapi/service/impl/CategoryServiceImpl.java index a98064b3..acdd51a9 100644 --- a/src/main/java/com/sopromadze/blogapi/service/impl/CategoryServiceImpl.java +++ b/src/main/java/com/sopromadze/blogapi/service/impl/CategoryServiceImpl.java @@ -26,58 +26,59 @@ @Service public class CategoryServiceImpl implements CategoryService { - @Autowired - private CategoryRepository categoryRepository; - - @Override - public PagedResponse getAllCategories(int page, int size) { - AppUtils.validatePageNumberAndSize(page, size); - - Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt"); - - Page categories = categoryRepository.findAll(pageable); - - List content = categories.getNumberOfElements() == 0 ? Collections.emptyList() : categories.getContent(); - - return new PagedResponse<>(content, categories.getNumber(), categories.getSize(), categories.getTotalElements(), - categories.getTotalPages(), categories.isLast()); - } - - @Override - public ResponseEntity getCategory(Long id) { - Category category = categoryRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Category", "id", id)); - return new ResponseEntity<>(category, HttpStatus.OK); - } - - @Override - public ResponseEntity addCategory(Category category, UserPrincipal currentUser) { - Category newCategory = categoryRepository.save(category); - return new ResponseEntity<>(newCategory, HttpStatus.CREATED); - } - - @Override - public ResponseEntity updateCategory(Long id, Category newCategory, UserPrincipal currentUser) { - Category category = categoryRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Category", "id", id)); - if (category.getCreatedBy().equals(currentUser.getId()) || currentUser.getAuthorities() - .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { - category.setName(newCategory.getName()); - Category updatedCategory = categoryRepository.save(category); - return new ResponseEntity<>(updatedCategory, HttpStatus.OK); - } - - throw new UnauthorizedException("You don't have permission to edit this category"); - } - - @Override - public ResponseEntity deleteCategory(Long id, UserPrincipal currentUser) { - Category category = categoryRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("category", "id", id)); - if (category.getCreatedBy().equals(currentUser.getId()) || currentUser.getAuthorities() - .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { - categoryRepository.deleteById(id); - return new ResponseEntity<>(new ApiResponse(Boolean.TRUE, "You successfully deleted category"), HttpStatus.OK); - } - throw new UnauthorizedException("You don't have permission to delete this category"); - } + @Autowired + private CategoryRepository categoryRepository; + + @Override + public PagedResponse getAllCategories(int page, int size) { + AppUtils.validatePageNumberAndSize(page, size); + + Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt"); + + Page categories = categoryRepository.findAll(pageable); + + List content = categories.getNumberOfElements() == 0 ? Collections.emptyList() : categories.getContent(); + + return new PagedResponse<>(content, categories.getNumber(), categories.getSize(), categories.getTotalElements(), + categories.getTotalPages(), categories.isLast()); + } + + @Override + public ResponseEntity getCategory(Long id) { + Category category = categoryRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Category", "id", id)); + return new ResponseEntity<>(category, HttpStatus.OK); + } + + @Override + public ResponseEntity addCategory(Category category, UserPrincipal currentUser) { + Category newCategory = categoryRepository.save(category); + return new ResponseEntity<>(newCategory, HttpStatus.CREATED); + } + + @Override + public ResponseEntity updateCategory(Long id, Category newCategory, UserPrincipal currentUser) { + Category category = categoryRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Category", "id", id)); + if (category.getCreatedBy().equals(currentUser.getId()) || currentUser.getAuthorities() + .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { + category.setName(newCategory.getName()); + Category updatedCategory = categoryRepository.save(category); + return new ResponseEntity<>(updatedCategory, HttpStatus.OK); + } + + throw new UnauthorizedException("You don't have permission to edit this category"); + } + + @Override + public ResponseEntity deleteCategory(Long id, UserPrincipal currentUser) { + Category category = categoryRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("category", "id", id)); + if (category.getCreatedBy().equals(currentUser.getId()) || currentUser.getAuthorities() + .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { + categoryRepository.deleteById(id); + return new ResponseEntity<>(new ApiResponse(Boolean.TRUE, "You successfully deleted category"), HttpStatus.OK); + } + throw new UnauthorizedException("You don't have permission to delete this category"); + } + } diff --git a/src/main/java/com/sopromadze/blogapi/service/impl/CommentServiceImpl.java b/src/main/java/com/sopromadze/blogapi/service/impl/CommentServiceImpl.java index 00258de7..18857d79 100644 --- a/src/main/java/com/sopromadze/blogapi/service/impl/CommentServiceImpl.java +++ b/src/main/java/com/sopromadze/blogapi/service/impl/CommentServiceImpl.java @@ -5,7 +5,7 @@ import com.sopromadze.blogapi.model.Comment; import com.sopromadze.blogapi.model.Post; import com.sopromadze.blogapi.model.role.RoleName; -import com.sopromadze.blogapi.model.user.User; +import com.sopromadze.blogapi.model.user.UserEntity; import com.sopromadze.blogapi.payload.ApiResponse; import com.sopromadze.blogapi.payload.CommentRequest; import com.sopromadze.blogapi.payload.PagedResponse; @@ -26,102 +26,104 @@ @Service public class CommentServiceImpl implements CommentService { - private static final String THIS_COMMENT = " this comment"; - - private static final String YOU_DON_T_HAVE_PERMISSION_TO = "You don't have permission to "; - - private static final String ID_STR = "id"; - - private static final String COMMENT_STR = "Comment"; - - private static final String POST_STR = "Post"; - - private static final String COMMENT_DOES_NOT_BELONG_TO_POST = "Comment does not belong to post"; - - @Autowired - private CommentRepository commentRepository; - - @Autowired - private PostRepository postRepository; - - @Autowired - private UserRepository userRepository; - - @Override - public PagedResponse getAllComments(Long postId, int page, int size) { - AppUtils.validatePageNumberAndSize(page, size); - Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt"); - - Page comments = commentRepository.findByPostId(postId, pageable); - - return new PagedResponse<>(comments.getContent(), comments.getNumber(), comments.getSize(), - comments.getTotalElements(), comments.getTotalPages(), comments.isLast()); - } - - @Override - public Comment addComment(CommentRequest commentRequest, Long postId, UserPrincipal currentUser) { - Post post = postRepository.findById(postId) - .orElseThrow(() -> new ResourceNotFoundException(POST_STR, ID_STR, postId)); - User user = userRepository.getUser(currentUser); - Comment comment = new Comment(commentRequest.getBody()); - comment.setUser(user); - comment.setPost(post); - comment.setName(currentUser.getUsername()); - comment.setEmail(currentUser.getEmail()); - return commentRepository.save(comment); - } - - @Override - public Comment getComment(Long postId, Long id) { - Post post = postRepository.findById(postId) - .orElseThrow(() -> new ResourceNotFoundException(POST_STR, ID_STR, postId)); - Comment comment = commentRepository.findById(id) - .orElseThrow(() -> new ResourceNotFoundException(COMMENT_STR, ID_STR, id)); - if (comment.getPost().getId().equals(post.getId())) { - return comment; - } - - throw new BlogapiException(HttpStatus.BAD_REQUEST, COMMENT_DOES_NOT_BELONG_TO_POST); - } - - @Override - public Comment updateComment(Long postId, Long id, CommentRequest commentRequest, - UserPrincipal currentUser) { - Post post = postRepository.findById(postId) - .orElseThrow(() -> new ResourceNotFoundException(POST_STR, ID_STR, postId)); - Comment comment = commentRepository.findById(id) - .orElseThrow(() -> new ResourceNotFoundException(COMMENT_STR, ID_STR, id)); - - if (!comment.getPost().getId().equals(post.getId())) { - throw new BlogapiException(HttpStatus.BAD_REQUEST, COMMENT_DOES_NOT_BELONG_TO_POST); - } - - if (comment.getUser().getId().equals(currentUser.getId()) - || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { - comment.setBody(commentRequest.getBody()); - return commentRepository.save(comment); - } - - throw new BlogapiException(HttpStatus.UNAUTHORIZED, YOU_DON_T_HAVE_PERMISSION_TO + "update" + THIS_COMMENT); - } - - @Override - public ApiResponse deleteComment(Long postId, Long id, UserPrincipal currentUser) { - Post post = postRepository.findById(postId) - .orElseThrow(() -> new ResourceNotFoundException(POST_STR, ID_STR, postId)); - Comment comment = commentRepository.findById(id) - .orElseThrow(() -> new ResourceNotFoundException(COMMENT_STR, ID_STR, id)); - - if (!comment.getPost().getId().equals(post.getId())) { - return new ApiResponse(Boolean.FALSE, COMMENT_DOES_NOT_BELONG_TO_POST); - } - - if (comment.getUser().getId().equals(currentUser.getId()) - || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { - commentRepository.deleteById(comment.getId()); - return new ApiResponse(Boolean.TRUE, "You successfully deleted comment"); - } - - throw new BlogapiException(HttpStatus.UNAUTHORIZED, YOU_DON_T_HAVE_PERMISSION_TO + "delete" + THIS_COMMENT); - } + + private static final String THIS_COMMENT = " this comment"; + + private static final String YOU_DON_T_HAVE_PERMISSION_TO = "You don't have permission to "; + + private static final String ID_STR = "id"; + + private static final String COMMENT_STR = "Comment"; + + private static final String POST_STR = "Post"; + + private static final String COMMENT_DOES_NOT_BELONG_TO_POST = "Comment does not belong to post"; + + @Autowired + private CommentRepository commentRepository; + + @Autowired + private PostRepository postRepository; + + @Autowired + private UserRepository userRepository; + + @Override + public PagedResponse getAllComments(Long postId, int page, int size) { + AppUtils.validatePageNumberAndSize(page, size); + Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt"); + + Page comments = commentRepository.findByPostId(postId, pageable); + + return new PagedResponse<>(comments.getContent(), comments.getNumber(), comments.getSize(), + comments.getTotalElements(), comments.getTotalPages(), comments.isLast()); + } + + @Override + public Comment addComment(CommentRequest commentRequest, Long postId, UserPrincipal currentUser) { + Post post = postRepository.findById(postId) + .orElseThrow(() -> new ResourceNotFoundException(POST_STR, ID_STR, postId)); + UserEntity userEntity = userRepository.getUser(currentUser); + Comment comment = new Comment(commentRequest.getBody()); + comment.setUser(userEntity); + comment.setPost(post); + comment.setName(currentUser.getUsername()); + comment.setEmail(currentUser.getEmail()); + return commentRepository.save(comment); + } + + @Override + public Comment getComment(Long postId, Long id) { + Post post = postRepository.findById(postId) + .orElseThrow(() -> new ResourceNotFoundException(POST_STR, ID_STR, postId)); + Comment comment = commentRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException(COMMENT_STR, ID_STR, id)); + if (comment.getPost().getId().equals(post.getId())) { + return comment; + } + + throw new BlogapiException(HttpStatus.BAD_REQUEST, COMMENT_DOES_NOT_BELONG_TO_POST); + } + + @Override + public Comment updateComment(Long postId, Long id, CommentRequest commentRequest, + UserPrincipal currentUser) { + Post post = postRepository.findById(postId) + .orElseThrow(() -> new ResourceNotFoundException(POST_STR, ID_STR, postId)); + Comment comment = commentRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException(COMMENT_STR, ID_STR, id)); + + if (!comment.getPost().getId().equals(post.getId())) { + throw new BlogapiException(HttpStatus.BAD_REQUEST, COMMENT_DOES_NOT_BELONG_TO_POST); + } + + if (comment.getUser().getId().equals(currentUser.getId()) + || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { + comment.setBody(commentRequest.getBody()); + return commentRepository.save(comment); + } + + throw new BlogapiException(HttpStatus.UNAUTHORIZED, YOU_DON_T_HAVE_PERMISSION_TO + "update" + THIS_COMMENT); + } + + @Override + public ApiResponse deleteComment(Long postId, Long id, UserPrincipal currentUser) { + Post post = postRepository.findById(postId) + .orElseThrow(() -> new ResourceNotFoundException(POST_STR, ID_STR, postId)); + Comment comment = commentRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException(COMMENT_STR, ID_STR, id)); + + if (!comment.getPost().getId().equals(post.getId())) { + return new ApiResponse(Boolean.FALSE, COMMENT_DOES_NOT_BELONG_TO_POST); + } + + if (comment.getUser().getId().equals(currentUser.getId()) + || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { + commentRepository.deleteById(comment.getId()); + return new ApiResponse(Boolean.TRUE, "You successfully deleted comment"); + } + + throw new BlogapiException(HttpStatus.UNAUTHORIZED, YOU_DON_T_HAVE_PERMISSION_TO + "delete" + THIS_COMMENT); + } + } diff --git a/src/main/java/com/sopromadze/blogapi/service/impl/CustomUserDetailsService.java b/src/main/java/com/sopromadze/blogapi/service/impl/CustomUserDetailsService.java new file mode 100644 index 00000000..8abb637d --- /dev/null +++ b/src/main/java/com/sopromadze/blogapi/service/impl/CustomUserDetailsService.java @@ -0,0 +1,44 @@ +package com.sopromadze.blogapi.service.impl; + +import com.sopromadze.blogapi.model.user.UserEntity; +import com.sopromadze.blogapi.repository.UserRepository; +import com.sopromadze.blogapi.security.UserPrincipal; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +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 java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class CustomUserDetailsService implements UserDetailsService { + + private final UserRepository userRepository; + + @Override + @Transactional + public UserDetails loadUserByUsername(String usernameOrEmail) { + UserEntity userEntity = userRepository.findByUsernameOrEmail(usernameOrEmail, usernameOrEmail) + .orElseThrow(() -> new UsernameNotFoundException(String.format("User not found with this username or email: %s", usernameOrEmail))); + + List authorities = userEntity.getRoles().stream() + .map(role -> new SimpleGrantedAuthority(role.getName().name())).collect(Collectors.toList()); + + return UserPrincipal.builder() + .id(userEntity.getId()) + .firstName(userEntity.getFirstName()) + .lastName(userEntity.getLastName()) + .username(userEntity.getUsername()) + .email(userEntity.getEmail()) + .password(userEntity.getPassword()) + .authorities(authorities) + .build(); + } + +} diff --git a/src/main/java/com/sopromadze/blogapi/service/impl/CustomUserDetailsServiceImpl.java b/src/main/java/com/sopromadze/blogapi/service/impl/CustomUserDetailsServiceImpl.java deleted file mode 100644 index 7b36a096..00000000 --- a/src/main/java/com/sopromadze/blogapi/service/impl/CustomUserDetailsServiceImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.sopromadze.blogapi.service.impl; - -import com.sopromadze.blogapi.model.user.User; -import com.sopromadze.blogapi.repository.UserRepository; -import com.sopromadze.blogapi.security.UserPrincipal; -import com.sopromadze.blogapi.service.CustomUserDetailsService; -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 jakarta.transaction.Transactional; - -@Service -public class CustomUserDetailsServiceImpl implements UserDetailsService, CustomUserDetailsService { - @Autowired - private UserRepository userRepository; - - @Override - @Transactional - public UserDetails loadUserByUsername(String usernameOrEmail) { - 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); - } - - @Override - @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); - } -} diff --git a/src/main/java/com/sopromadze/blogapi/service/impl/PhotoServiceImpl.java b/src/main/java/com/sopromadze/blogapi/service/impl/PhotoServiceImpl.java index dfa90b86..e4482521 100644 --- a/src/main/java/com/sopromadze/blogapi/service/impl/PhotoServiceImpl.java +++ b/src/main/java/com/sopromadze/blogapi/service/impl/PhotoServiceImpl.java @@ -27,116 +27,114 @@ import java.util.Collections; import java.util.List; -import static com.sopromadze.blogapi.utils.AppConstants.ALBUM; -import static com.sopromadze.blogapi.utils.AppConstants.CREATED_AT; -import static com.sopromadze.blogapi.utils.AppConstants.ID; -import static com.sopromadze.blogapi.utils.AppConstants.PHOTO; +import static com.sopromadze.blogapi.utils.AppConstants.*; @Service public class PhotoServiceImpl implements PhotoService { - @Autowired - private PhotoRepository photoRepository; - - @Autowired - private AlbumRepository albumRepository; - - @Override - public PagedResponse getAllPhotos(int page, int size) { - AppUtils.validatePageNumberAndSize(page, size); - - Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); - Page photos = photoRepository.findAll(pageable); - - List photoResponses = new ArrayList<>(photos.getContent().size()); - for (Photo photo : photos.getContent()) { - photoResponses.add(new PhotoResponse(photo.getId(), photo.getTitle(), photo.getUrl(), - photo.getThumbnailUrl(), photo.getAlbum().getId())); - } - - 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()); - - } - - @Override - public PhotoResponse getPhoto(Long id) { - Photo photo = photoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(PHOTO, ID, id)); - - return new PhotoResponse(photo.getId(), photo.getTitle(), photo.getUrl(), - photo.getThumbnailUrl(), photo.getAlbum().getId()); - } - - @Override - public PhotoResponse 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 PhotoResponse(updatedPhoto.getId(), updatedPhoto.getTitle(), - updatedPhoto.getUrl(), updatedPhoto.getThumbnailUrl(), updatedPhoto.getAlbum().getId()); - } - - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to update this photo"); - - throw new UnauthorizedException(apiResponse); - } - - @Override - public PhotoResponse 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 PhotoResponse(newPhoto.getId(), newPhoto.getTitle(), newPhoto.getUrl(), - newPhoto.getThumbnailUrl(), newPhoto.getAlbum().getId()); - } - - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to add photo in this album"); - - throw new UnauthorizedException(apiResponse); - } - - @Override - public ApiResponse 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 ApiResponse(Boolean.TRUE, "Photo deleted successfully"); - } - - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to delete this photo"); - - throw new UnauthorizedException(apiResponse); - } - - @Override - public PagedResponse getAllPhotosByAlbum(Long albumId, int page, int size) { - AppUtils.validatePageNumberAndSize(page, size); - - Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, AppConstants.CREATED_AT); - - Page photos = photoRepository.findByAlbumId(albumId, pageable); - - List photoResponses = new ArrayList<>(photos.getContent().size()); - 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()); - } + @Autowired + private PhotoRepository photoRepository; + + @Autowired + private AlbumRepository albumRepository; + + @Override + public PagedResponse getAllPhotos(int page, int size) { + AppUtils.validatePageNumberAndSize(page, size); + + Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); + Page photos = photoRepository.findAll(pageable); + + List photoResponses = new ArrayList<>(photos.getContent().size()); + for (Photo photo : photos.getContent()) { + photoResponses.add(new PhotoResponse(photo.getId(), photo.getTitle(), photo.getUrl(), + photo.getThumbnailUrl(), photo.getAlbum().getId())); + } + + 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()); + + } + + @Override + public PhotoResponse getPhoto(Long id) { + Photo photo = photoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(PHOTO, ID, id)); + + return new PhotoResponse(photo.getId(), photo.getTitle(), photo.getUrl(), + photo.getThumbnailUrl(), photo.getAlbum().getId()); + } + + @Override + public PhotoResponse 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 PhotoResponse(updatedPhoto.getId(), updatedPhoto.getTitle(), + updatedPhoto.getUrl(), updatedPhoto.getThumbnailUrl(), updatedPhoto.getAlbum().getId()); + } + + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to update this photo"); + + throw new UnauthorizedException(apiResponse); + } + + @Override + public PhotoResponse 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 PhotoResponse(newPhoto.getId(), newPhoto.getTitle(), newPhoto.getUrl(), + newPhoto.getThumbnailUrl(), newPhoto.getAlbum().getId()); + } + + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to add photo in this album"); + + throw new UnauthorizedException(apiResponse); + } + + @Override + public ApiResponse 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 ApiResponse(Boolean.TRUE, "Photo deleted successfully"); + } + + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to delete this photo"); + + throw new UnauthorizedException(apiResponse); + } + + @Override + public PagedResponse getAllPhotosByAlbum(Long albumId, int page, int size) { + AppUtils.validatePageNumberAndSize(page, size); + + Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, AppConstants.CREATED_AT); + + Page photos = photoRepository.findByAlbumId(albumId, pageable); + + List photoResponses = new ArrayList<>(photos.getContent().size()); + 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()); + } + } diff --git a/src/main/java/com/sopromadze/blogapi/service/impl/PostServiceImpl.java b/src/main/java/com/sopromadze/blogapi/service/impl/PostServiceImpl.java index 8666ec58..818bd8b3 100644 --- a/src/main/java/com/sopromadze/blogapi/service/impl/PostServiceImpl.java +++ b/src/main/java/com/sopromadze/blogapi/service/impl/PostServiceImpl.java @@ -7,7 +7,7 @@ import com.sopromadze.blogapi.model.Post; import com.sopromadze.blogapi.model.Tag; import com.sopromadze.blogapi.model.role.RoleName; -import com.sopromadze.blogapi.model.user.User; +import com.sopromadze.blogapi.model.user.UserEntity; import com.sopromadze.blogapi.payload.ApiResponse; import com.sopromadze.blogapi.payload.PagedResponse; import com.sopromadze.blogapi.payload.PostRequest; @@ -32,174 +32,171 @@ import java.util.Collections; import java.util.List; -import static com.sopromadze.blogapi.utils.AppConstants.CATEGORY; -import static com.sopromadze.blogapi.utils.AppConstants.CREATED_AT; -import static com.sopromadze.blogapi.utils.AppConstants.ID; -import static com.sopromadze.blogapi.utils.AppConstants.POST; -import static com.sopromadze.blogapi.utils.AppConstants.TAG; -import static com.sopromadze.blogapi.utils.AppConstants.USER; +import static com.sopromadze.blogapi.utils.AppConstants.*; @Service public class PostServiceImpl implements PostService { - @Autowired - private PostRepository postRepository; - @Autowired - private UserRepository userRepository; + @Autowired + private PostRepository postRepository; - @Autowired - private CategoryRepository categoryRepository; + @Autowired + private UserRepository userRepository; - @Autowired - private TagRepository tagRepository; + @Autowired + private CategoryRepository categoryRepository; - @Override - public PagedResponse getAllPosts(int page, int size) { - validatePageNumberAndSize(page, size); + @Autowired + private TagRepository tagRepository; - Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); + @Override + public PagedResponse getAllPosts(int page, int size) { + validatePageNumberAndSize(page, size); - Page posts = postRepository.findAll(pageable); + Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); - List content = posts.getNumberOfElements() == 0 ? Collections.emptyList() : posts.getContent(); + Page posts = postRepository.findAll(pageable); - return new PagedResponse<>(content, posts.getNumber(), posts.getSize(), posts.getTotalElements(), - posts.getTotalPages(), posts.isLast()); - } + List content = posts.getNumberOfElements() == 0 ? Collections.emptyList() : posts.getContent(); - @Override - public PagedResponse getPostsByCreatedBy(String username, int page, int size) { - validatePageNumberAndSize(page, size); - User user = userRepository.getUserByName(username); - Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); - Page posts = postRepository.findByCreatedBy(user.getId(), pageable); + return new PagedResponse<>(content, posts.getNumber(), posts.getSize(), posts.getTotalElements(), + posts.getTotalPages(), posts.isLast()); + } - List content = posts.getNumberOfElements() == 0 ? Collections.emptyList() : posts.getContent(); + @Override + public PagedResponse getPostsByCreatedBy(String username, int page, int size) { + validatePageNumberAndSize(page, size); + UserEntity userEntity = userRepository.getUserByName(username); + Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); + Page posts = postRepository.findByCreatedBy(userEntity.getId(), pageable); - return new PagedResponse<>(content, posts.getNumber(), posts.getSize(), posts.getTotalElements(), - posts.getTotalPages(), posts.isLast()); - } + List content = posts.getNumberOfElements() == 0 ? Collections.emptyList() : posts.getContent(); - @Override - public PagedResponse getPostsByCategory(Long id, int page, int size) { - AppUtils.validatePageNumberAndSize(page, size); - Category category = categoryRepository.findById(id) - .orElseThrow(() -> new ResourceNotFoundException(CATEGORY, ID, id)); + return new PagedResponse<>(content, posts.getNumber(), posts.getSize(), posts.getTotalElements(), + posts.getTotalPages(), posts.isLast()); + } - Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); - Page posts = postRepository.findByCategory(category.getId(), pageable); + @Override + public PagedResponse getPostsByCategory(Long id, int page, int size) { + AppUtils.validatePageNumberAndSize(page, size); + Category category = categoryRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException(CATEGORY, ID, id)); - List content = posts.getNumberOfElements() == 0 ? Collections.emptyList() : posts.getContent(); + Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); + Page posts = postRepository.findByCategory(category.getCategoryId(), pageable); - return new PagedResponse<>(content, posts.getNumber(), posts.getSize(), posts.getTotalElements(), - posts.getTotalPages(), posts.isLast()); - } + List content = posts.getNumberOfElements() == 0 ? Collections.emptyList() : posts.getContent(); - @Override - public PagedResponse getPostsByTag(Long id, int page, int size) { - AppUtils.validatePageNumberAndSize(page, size); + return new PagedResponse<>(content, posts.getNumber(), posts.getSize(), posts.getTotalElements(), + posts.getTotalPages(), posts.isLast()); + } - Tag tag = tagRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(TAG, ID, id)); + @Override + public PagedResponse getPostsByTag(Long id, int page, int size) { + AppUtils.validatePageNumberAndSize(page, size); - Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); + Tag tag = tagRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(TAG, ID, id)); - Page posts = postRepository.findByTags(Collections.singletonList(tag), pageable); + Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); - List content = posts.getNumberOfElements() == 0 ? Collections.emptyList() : posts.getContent(); + Page posts = postRepository.findByTags(Collections.singletonList(tag), pageable); - return new PagedResponse<>(content, posts.getNumber(), posts.getSize(), posts.getTotalElements(), - posts.getTotalPages(), posts.isLast()); - } + List content = posts.getNumberOfElements() == 0 ? Collections.emptyList() : posts.getContent(); - @Override - public Post updatePost(Long id, PostRequest newPostRequest, UserPrincipal currentUser) { - Post post = postRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(POST, ID, id)); - Category category = categoryRepository.findById(newPostRequest.getCategoryId()) - .orElseThrow(() -> new ResourceNotFoundException(CATEGORY, ID, newPostRequest.getCategoryId())); - if (post.getUser().getId().equals(currentUser.getId()) - || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { - post.setTitle(newPostRequest.getTitle()); - post.setBody(newPostRequest.getBody()); - post.setCategory(category); - return postRepository.save(post); - } - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to edit this post"); + return new PagedResponse<>(content, posts.getNumber(), posts.getSize(), posts.getTotalElements(), + posts.getTotalPages(), posts.isLast()); + } - throw new UnauthorizedException(apiResponse); - } + @Override + public Post updatePost(Long id, PostRequest newPostRequest, UserPrincipal currentUser) { + Post post = postRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(POST, ID, id)); + Category category = categoryRepository.findById(newPostRequest.getCategoryId()) + .orElseThrow(() -> new ResourceNotFoundException(CATEGORY, ID, newPostRequest.getCategoryId())); + if (post.getUser().getId().equals(currentUser.getId()) + || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { + post.setTitle(newPostRequest.getTitle()); + post.setBody(newPostRequest.getBody()); + post.setCategory(category); + return postRepository.save(post); + } + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to edit this post"); - @Override - public ApiResponse 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 ApiResponse(Boolean.TRUE, "You successfully deleted post"); - } + throw new UnauthorizedException(apiResponse); + } - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to delete this post"); + @Override + public ApiResponse 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 ApiResponse(Boolean.TRUE, "You successfully deleted post"); + } - throw new UnauthorizedException(apiResponse); - } + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to delete this post"); - @Override - public PostResponse addPost(PostRequest postRequest, UserPrincipal currentUser) { - User user = userRepository.findById(currentUser.getId()) - .orElseThrow(() -> new ResourceNotFoundException(USER, ID, 1L)); - Category category = categoryRepository.findById(postRequest.getCategoryId()) - .orElseThrow(() -> new ResourceNotFoundException(CATEGORY, ID, postRequest.getCategoryId())); + throw new UnauthorizedException(apiResponse); + } - List tags = new ArrayList<>(postRequest.getTags().size()); + @Override + public PostResponse addPost(PostRequest postRequest, UserPrincipal currentUser) { + UserEntity userEntity = userRepository.findById(currentUser.getId()) + .orElseThrow(() -> new ResourceNotFoundException(USER, ID, 1L)); + Category category = categoryRepository.findById(postRequest.getCategoryId()) + .orElseThrow(() -> new ResourceNotFoundException(CATEGORY, ID, postRequest.getCategoryId())); - for (String name : postRequest.getTags()) { - Tag tag = tagRepository.findByName(name); - tag = tag == null ? tagRepository.save(new Tag(name)) : tag; + List tags = new ArrayList<>(postRequest.getTags().size()); - tags.add(tag); - } + for (String name : postRequest.getTags()) { + Tag tag = tagRepository.findByName(name); + tag = tag == null ? tagRepository.save(new Tag(name)) : tag; - Post post = new Post(); - post.setBody(postRequest.getBody()); - post.setTitle(postRequest.getTitle()); - post.setCategory(category); - post.setUser(user); - post.setTags(tags); + tags.add(tag); + } - Post newPost = postRepository.save(post); + Post post = new Post(); + post.setBody(postRequest.getBody()); + post.setTitle(postRequest.getTitle()); + post.setCategory(category); + post.setUser(userEntity); + post.setTags(tags); - PostResponse postResponse = new PostResponse(); + Post newPost = postRepository.save(post); - postResponse.setTitle(newPost.getTitle()); - postResponse.setBody(newPost.getBody()); - postResponse.setCategory(newPost.getCategory().getName()); + PostResponse postResponse = new PostResponse(); - List tagNames = new ArrayList<>(newPost.getTags().size()); + postResponse.setTitle(newPost.getTitle()); + postResponse.setBody(newPost.getBody()); + postResponse.setCategory(newPost.getCategory().getName()); - for (Tag tag : newPost.getTags()) { - tagNames.add(tag.getName()); - } + List tagNames = new ArrayList<>(newPost.getTags().size()); - postResponse.setTags(tagNames); + for (Tag tag : newPost.getTags()) { + tagNames.add(tag.getName()); + } - return postResponse; - } + postResponse.setTags(tagNames); - @Override - public Post getPost(Long id) { - return postRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(POST, ID, id)); - } + return postResponse; + } - private void validatePageNumberAndSize(int page, int size) { - if (page < 0) { - throw new BadRequestException("Page number cannot be less than zero."); - } + @Override + public Post getPost(Long id) { + return postRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(POST, ID, id)); + } - if (size < 0) { - throw new BadRequestException("Size number cannot be less than zero."); - } + 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); + } + } - 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/impl/TagServiceImpl.java b/src/main/java/com/sopromadze/blogapi/service/impl/TagServiceImpl.java index 18488a23..7ae1cc3e 100644 --- a/src/main/java/com/sopromadze/blogapi/service/impl/TagServiceImpl.java +++ b/src/main/java/com/sopromadze/blogapi/service/impl/TagServiceImpl.java @@ -24,58 +24,59 @@ @Service public class TagServiceImpl implements TagService { - @Autowired - private TagRepository tagRepository; - - @Override - public PagedResponse getAllTags(int page, int size) { - AppUtils.validatePageNumberAndSize(page, size); - - Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt"); - - Page tags = tagRepository.findAll(pageable); - - List content = tags.getNumberOfElements() == 0 ? Collections.emptyList() : tags.getContent(); - - return new PagedResponse<>(content, tags.getNumber(), tags.getSize(), tags.getTotalElements(), tags.getTotalPages(), tags.isLast()); - } - - @Override - public Tag getTag(Long id) { - return tagRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Tag", "id", id)); - } - - @Override - public Tag addTag(Tag tag, UserPrincipal currentUser) { - return tagRepository.save(tag); - } - - @Override - public Tag updateTag(Long id, Tag newTag, UserPrincipal currentUser) { - Tag tag = tagRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Tag", "id", id)); - if (tag.getCreatedBy().equals(currentUser.getId()) || currentUser.getAuthorities() - .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { - tag.setName(newTag.getName()); - return tagRepository.save(tag); - } - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to edit this tag"); - - throw new UnauthorizedException(apiResponse); - } - - @Override - public ApiResponse deleteTag(Long id, UserPrincipal currentUser) { - Tag tag = tagRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Tag", "id", id)); - if (tag.getCreatedBy().equals(currentUser.getId()) || currentUser.getAuthorities() - .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { - tagRepository.deleteById(id); - return new ApiResponse(Boolean.TRUE, "You successfully deleted tag"); - } - - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to delete this tag"); - - throw new UnauthorizedException(apiResponse); - } + @Autowired + private TagRepository tagRepository; + + @Override + public PagedResponse getAllTags(int page, int size) { + AppUtils.validatePageNumberAndSize(page, size); + + Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "createdAt"); + + Page tags = tagRepository.findAll(pageable); + + List content = tags.getNumberOfElements() == 0 ? Collections.emptyList() : tags.getContent(); + + return new PagedResponse<>(content, tags.getNumber(), tags.getSize(), tags.getTotalElements(), tags.getTotalPages(), tags.isLast()); + } + + @Override + public Tag getTag(Long id) { + return tagRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Tag", "id", id)); + } + + @Override + public Tag addTag(Tag tag, UserPrincipal currentUser) { + return tagRepository.save(tag); + } + + @Override + public Tag updateTag(Long id, Tag newTag, UserPrincipal currentUser) { + Tag tag = tagRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Tag", "id", id)); + if (tag.getCreatedBy().equals(currentUser.getId()) || currentUser.getAuthorities() + .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { + tag.setName(newTag.getName()); + return tagRepository.save(tag); + } + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to edit this tag"); + + throw new UnauthorizedException(apiResponse); + } + + @Override + public ApiResponse deleteTag(Long id, UserPrincipal currentUser) { + Tag tag = tagRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Tag", "id", id)); + if (tag.getCreatedBy().equals(currentUser.getId()) || currentUser.getAuthorities() + .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { + tagRepository.deleteById(id); + return new ApiResponse(Boolean.TRUE, "You successfully deleted tag"); + } + + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to delete this tag"); + + throw new UnauthorizedException(apiResponse); + } + } diff --git a/src/main/java/com/sopromadze/blogapi/service/impl/TodoServiceImpl.java b/src/main/java/com/sopromadze/blogapi/service/impl/TodoServiceImpl.java index 9ef3b2e8..38918279 100644 --- a/src/main/java/com/sopromadze/blogapi/service/impl/TodoServiceImpl.java +++ b/src/main/java/com/sopromadze/blogapi/service/impl/TodoServiceImpl.java @@ -3,11 +3,11 @@ import com.sopromadze.blogapi.exception.BadRequestException; import com.sopromadze.blogapi.exception.ResourceNotFoundException; import com.sopromadze.blogapi.exception.UnauthorizedException; -import com.sopromadze.blogapi.model.Todo; -import com.sopromadze.blogapi.model.user.User; +import com.sopromadze.blogapi.infrastructure.persistence.postgresql.entity.TodoEntity; +import com.sopromadze.blogapi.infrastructure.persistence.postgresql.repository.TodoRepository; +import com.sopromadze.blogapi.model.user.UserEntity; 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.service.TodoService; @@ -22,125 +22,123 @@ import java.util.Collections; import java.util.List; -import static com.sopromadze.blogapi.utils.AppConstants.CREATED_AT; -import static com.sopromadze.blogapi.utils.AppConstants.ID; -import static com.sopromadze.blogapi.utils.AppConstants.TODO; -import static com.sopromadze.blogapi.utils.AppConstants.YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION; +import static com.sopromadze.blogapi.utils.AppConstants.*; @Service public class TodoServiceImpl implements TodoService { - @Autowired - private TodoRepository todoRepository; + @Autowired + private TodoRepository todoRepository; - @Autowired - private UserRepository userRepository; + @Autowired + private UserRepository userRepository; - @Override - public Todo completeTodo(Long id, UserPrincipal currentUser) { - Todo todo = todoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(TODO, ID, id)); + @Override + public TodoEntity completeTodo(Long id, UserPrincipal currentUser) { + TodoEntity todoEntity = todoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(TODO, ID, id)); - User user = userRepository.getUser(currentUser); + UserEntity userEntity = userRepository.getUser(currentUser); - if (todo.getUser().getId().equals(user.getId())) { - todo.setCompleted(Boolean.TRUE); - return todoRepository.save(todo); - } + if (todoEntity.getUser().getId().equals(userEntity.getId())) { + todoEntity.setCompleted(Boolean.TRUE); + return todoRepository.save(todoEntity); + } - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); - throw new UnauthorizedException(apiResponse); - } + throw new UnauthorizedException(apiResponse); + } - @Override - public Todo unCompleteTodo(Long id, UserPrincipal currentUser) { - Todo todo = todoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(TODO, ID, id)); - User user = userRepository.getUser(currentUser); - if (todo.getUser().getId().equals(user.getId())) { - todo.setCompleted(Boolean.FALSE); - return todoRepository.save(todo); - } + @Override + public TodoEntity unCompleteTodo(Long id, UserPrincipal currentUser) { + TodoEntity todoEntity = todoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(TODO, ID, id)); + UserEntity userEntity = userRepository.getUser(currentUser); + if (todoEntity.getUser().getId().equals(userEntity.getId())) { + todoEntity.setCompleted(Boolean.FALSE); + return todoRepository.save(todoEntity); + } - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); - throw new UnauthorizedException(apiResponse); - } + throw new UnauthorizedException(apiResponse); + } - @Override - public PagedResponse getAllTodos(UserPrincipal currentUser, int page, int size) { - validatePageNumberAndSize(page, size); - Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); + @Override + public PagedResponse getAllTodos(UserPrincipal currentUser, int page, int size) { + validatePageNumberAndSize(page, size); + Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT); - Page todos = todoRepository.findByCreatedBy(currentUser.getId(), pageable); + Page todos = todoRepository.findByCreatedBy(currentUser.getId(), pageable); - List content = todos.getNumberOfElements() == 0 ? Collections.emptyList() : todos.getContent(); + List content = todos.getNumberOfElements() == 0 ? Collections.emptyList() : todos.getContent(); - return new PagedResponse<>(content, todos.getNumber(), todos.getSize(), todos.getTotalElements(), - todos.getTotalPages(), todos.isLast()); - } + return new PagedResponse<>(content, todos.getNumber(), todos.getSize(), todos.getTotalElements(), + todos.getTotalPages(), todos.isLast()); + } - @Override - public Todo addTodo(Todo todo, UserPrincipal currentUser) { - User user = userRepository.getUser(currentUser); - todo.setUser(user); - return todoRepository.save(todo); - } + @Override + public TodoEntity addTodo(TodoEntity todoEntity, String username) { + userRepository.findByUsername(username) + .ifPresent(todoEntity::setUser); + return todoRepository.save(todoEntity); + } - @Override - public Todo getTodo(Long id, UserPrincipal currentUser) { - User user = userRepository.getUser(currentUser); - Todo todo = todoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(TODO, ID, id)); + @Override + public TodoEntity getTodo(Long id, UserPrincipal currentUser) { + UserEntity userEntity = userRepository.getUser(currentUser); + TodoEntity todoEntity = todoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(TODO, ID, id)); - if (todo.getUser().getId().equals(user.getId())) { - return todo; - } + if (todoEntity.getUser().getId().equals(userEntity.getId())) { + return todoEntity; + } - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); - throw new UnauthorizedException(apiResponse); - } + throw new UnauthorizedException(apiResponse); + } - @Override - public Todo updateTodo(Long id, Todo newTodo, UserPrincipal currentUser) { - User user = userRepository.getUser(currentUser); - Todo todo = todoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(TODO, ID, id)); - if (todo.getUser().getId().equals(user.getId())) { - todo.setTitle(newTodo.getTitle()); - todo.setCompleted(newTodo.getCompleted()); - return todoRepository.save(todo); - } + @Override + public TodoEntity updateTodo(Long id, TodoEntity newTodoEntity, UserPrincipal currentUser) { + UserEntity userEntity = userRepository.getUser(currentUser); + TodoEntity todoEntity = todoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(TODO, ID, id)); + if (todoEntity.getUser().getId().equals(userEntity.getId())) { + todoEntity.setTitle(newTodoEntity.getTitle()); + todoEntity.setCompleted(newTodoEntity.isCompleted()); + return todoRepository.save(todoEntity); + } - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); - throw new UnauthorizedException(apiResponse); - } + throw new UnauthorizedException(apiResponse); + } - @Override - public ApiResponse deleteTodo(Long id, UserPrincipal currentUser) { - User user = userRepository.getUser(currentUser); - Todo todo = todoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(TODO, ID, id)); + @Override + public ApiResponse deleteTodo(Long id, UserPrincipal currentUser) { + UserEntity userEntity = userRepository.getUser(currentUser); + TodoEntity todoEntity = todoRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(TODO, ID, id)); - if (todo.getUser().getId().equals(user.getId())) { - todoRepository.deleteById(id); - return new ApiResponse(Boolean.TRUE, "You successfully deleted todo"); - } + if (todoEntity.getUser().getId().equals(userEntity.getId())) { + todoRepository.deleteById(id); + return new ApiResponse(Boolean.TRUE, "You successfully deleted todo"); + } - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION); - throw new UnauthorizedException(apiResponse); - } + throw new UnauthorizedException(apiResponse); + } - private void validatePageNumberAndSize(int page, int size) { - if (page < 0) { - throw new BadRequestException("Page number cannot be less than zero."); - } + 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 < 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); + } + } - 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/impl/UserServiceImpl.java b/src/main/java/com/sopromadze/blogapi/service/impl/UserServiceImpl.java index d740f27b..3823ed8a 100644 --- a/src/main/java/com/sopromadze/blogapi/service/impl/UserServiceImpl.java +++ b/src/main/java/com/sopromadze/blogapi/service/impl/UserServiceImpl.java @@ -1,21 +1,13 @@ package com.sopromadze.blogapi.service.impl; -import com.sopromadze.blogapi.exception.AccessDeniedException; -import com.sopromadze.blogapi.exception.AppException; -import com.sopromadze.blogapi.exception.BadRequestException; -import com.sopromadze.blogapi.exception.ResourceNotFoundException; -import com.sopromadze.blogapi.exception.UnauthorizedException; +import com.sopromadze.blogapi.exception.*; import com.sopromadze.blogapi.model.role.Role; import com.sopromadze.blogapi.model.role.RoleName; import com.sopromadze.blogapi.model.user.Address; import com.sopromadze.blogapi.model.user.Company; import com.sopromadze.blogapi.model.user.Geo; -import com.sopromadze.blogapi.model.user.User; -import com.sopromadze.blogapi.payload.ApiResponse; -import com.sopromadze.blogapi.payload.InfoRequest; -import com.sopromadze.blogapi.payload.UserIdentityAvailability; -import com.sopromadze.blogapi.payload.UserProfile; -import com.sopromadze.blogapi.payload.UserSummary; +import com.sopromadze.blogapi.model.user.UserEntity; +import com.sopromadze.blogapi.payload.*; import com.sopromadze.blogapi.repository.PostRepository; import com.sopromadze.blogapi.repository.RoleRepository; import com.sopromadze.blogapi.repository.UserRepository; @@ -32,154 +24,167 @@ @Service public class UserServiceImpl implements UserService { - @Autowired - private UserRepository userRepository; - - @Autowired - private PostRepository postRepository; - - @Autowired - private RoleRepository roleRepository; - - @Autowired - private PasswordEncoder passwordEncoder; - - @Override - public UserSummary getCurrentUser(UserPrincipal currentUser) { - return new UserSummary(currentUser.getId(), currentUser.getUsername(), currentUser.getFirstName(), - currentUser.getLastName()); - } - - @Override - public UserIdentityAvailability checkUsernameAvailability(String username) { - Boolean isAvailable = !userRepository.existsByUsername(username); - return new UserIdentityAvailability(isAvailable); - } - - @Override - public UserIdentityAvailability checkEmailAvailability(String email) { - Boolean isAvailable = !userRepository.existsByEmail(email); - return new UserIdentityAvailability(isAvailable); - } - - @Override - public UserProfile getUserProfile(String username) { - User user = userRepository.getUserByName(username); - - Long postCount = postRepository.countByCreatedBy(user.getId()); - - return new UserProfile(user.getId(), user.getUsername(), user.getFirstName(), user.getLastName(), - user.getCreatedAt(), user.getEmail(), user.getAddress(), user.getPhone(), user.getWebsite(), - user.getCompany(), postCount); - } - - @Override - public User addUser(User user) { - if (userRepository.existsByUsername(user.getUsername())) { - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "Username is already taken"); - throw new BadRequestException(apiResponse); - } - - if (userRepository.existsByEmail(user.getEmail())) { - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "Email is already taken"); - throw new BadRequestException(apiResponse); - } - - List roles = new ArrayList<>(); - roles.add( - roleRepository.findByName(RoleName.ROLE_USER).orElseThrow(() -> new AppException("User role not set"))); - user.setRoles(roles); - - user.setPassword(passwordEncoder.encode(user.getPassword())); - return userRepository.save(user); - } - - @Override - public User updateUser(User newUser, String username, UserPrincipal currentUser) { - User user = userRepository.getUserByName(username); - if (user.getId().equals(currentUser.getId()) - || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { - user.setFirstName(newUser.getFirstName()); - user.setLastName(newUser.getLastName()); - user.setPassword(passwordEncoder.encode(newUser.getPassword())); - user.setAddress(newUser.getAddress()); - user.setPhone(newUser.getPhone()); - user.setWebsite(newUser.getWebsite()); - user.setCompany(newUser.getCompany()); - - return userRepository.save(user); - - } - - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to update profile of: " + username); - throw new UnauthorizedException(apiResponse); - - } - - @Override - public ApiResponse deleteUser(String username, UserPrincipal currentUser) { - User user = userRepository.findByUsername(username) - .orElseThrow(() -> new ResourceNotFoundException("User", "id", username)); - if (!user.getId().equals(currentUser.getId()) || !currentUser.getAuthorities() - .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to delete profile of: " + username); - throw new AccessDeniedException(apiResponse); - } - - userRepository.deleteById(user.getId()); - - return new ApiResponse(Boolean.TRUE, "You successfully deleted profile of: " + username); - } - - @Override - public ApiResponse giveAdmin(String username) { - User user = userRepository.getUserByName(username); - List roles = new ArrayList<>(); - roles.add(roleRepository.findByName(RoleName.ROLE_ADMIN) - .orElseThrow(() -> new AppException("User role not set"))); - roles.add( - roleRepository.findByName(RoleName.ROLE_USER).orElseThrow(() -> new AppException("User role not set"))); - user.setRoles(roles); - userRepository.save(user); - return new ApiResponse(Boolean.TRUE, "You gave ADMIN role to user: " + username); - } - - @Override - public ApiResponse removeAdmin(String username) { - User user = userRepository.getUserByName(username); - List roles = new ArrayList<>(); - roles.add( - roleRepository.findByName(RoleName.ROLE_USER).orElseThrow(() -> new AppException("User role not set"))); - user.setRoles(roles); - userRepository.save(user); - return new ApiResponse(Boolean.TRUE, "You took ADMIN role from user: " + username); - } - - @Override - public UserProfile setOrUpdateInfo(UserPrincipal currentUser, InfoRequest infoRequest) { - User user = userRepository.findByUsername(currentUser.getUsername()) - .orElseThrow(() -> new ResourceNotFoundException("User", "username", currentUser.getUsername())); - Geo geo = new Geo(infoRequest.getLat(), infoRequest.getLng()); - Address address = new Address(infoRequest.getStreet(), infoRequest.getSuite(), infoRequest.getCity(), - infoRequest.getZipcode(), geo); - Company company = new Company(infoRequest.getCompanyName(), infoRequest.getCatchPhrase(), infoRequest.getBs()); - if (user.getId().equals(currentUser.getId()) - || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { - user.setAddress(address); - user.setCompany(company); - user.setWebsite(infoRequest.getWebsite()); - user.setPhone(infoRequest.getPhone()); - User updatedUser = userRepository.save(user); - - Long postCount = postRepository.countByCreatedBy(updatedUser.getId()); - - return new UserProfile(updatedUser.getId(), updatedUser.getUsername(), - updatedUser.getFirstName(), updatedUser.getLastName(), updatedUser.getCreatedAt(), - updatedUser.getEmail(), updatedUser.getAddress(), updatedUser.getPhone(), updatedUser.getWebsite(), - updatedUser.getCompany(), postCount); - } - - ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to update users profile", HttpStatus.FORBIDDEN); - throw new AccessDeniedException(apiResponse); - } + + @Autowired + private UserRepository userRepository; + + @Autowired + private PostRepository postRepository; + + @Autowired + private RoleRepository roleRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Override + public UserSummary getCurrentUser(UserPrincipal currentUser) { + return new UserSummary(currentUser.getId(), currentUser.getUsername(), currentUser.getFirstName(), + currentUser.getLastName()); + } + + @Override + public UserIdentityAvailability checkUsernameAvailability(String username) { + Boolean isAvailable = !userRepository.existsByUsername(username); + return new UserIdentityAvailability(isAvailable); + } + + @Override + public UserIdentityAvailability checkEmailAvailability(String email) { + Boolean isAvailable = !userRepository.existsByEmail(email); + return new UserIdentityAvailability(isAvailable); + } + + @Override + public UserProfile getUserProfile(String username) { + UserEntity userEntity = userRepository.getUserByName(username); + + Long postCount = postRepository.countByCreatedBy(userEntity.getId()); + + return new UserProfile(userEntity.getId(), userEntity.getUsername(), userEntity.getFirstName(), userEntity.getLastName(), + userEntity.getCreatedAt(), userEntity.getEmail(), userEntity.getAddress(), userEntity.getPhone(), userEntity.getWebsite(), + userEntity.getCompany(), postCount); + } + + @Override + public UserEntity addUser(UserEntity userEntity) { + if (userRepository.existsByUsername(userEntity.getUsername())) { + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "Username is already taken"); + throw new BadRequestException(apiResponse); + } + + if (userRepository.existsByEmail(userEntity.getEmail())) { + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "Email is already taken"); + throw new BadRequestException(apiResponse); + } + + List roles = new ArrayList<>(); + roles.add(roleRepository.findByName(RoleName.ROLE_USER).orElseThrow(() -> new AppException("User role not set"))); + userEntity.setRoles(roles); + + userEntity.setPassword(passwordEncoder.encode(userEntity.getPassword())); + return userRepository.save(userEntity); + } + + @Override + public UserEntity updateUser(UserEntity newUserEntity, String username, UserPrincipal currentUser) { + UserEntity userEntity = userRepository.getUserByName(username); + if (userEntity.getId().equals(currentUser.getId()) + || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { + userEntity.setFirstName(newUserEntity.getFirstName()); + userEntity.setLastName(newUserEntity.getLastName()); + userEntity.setPassword(passwordEncoder.encode(newUserEntity.getPassword())); + userEntity.setAddress(newUserEntity.getAddress()); + userEntity.setPhone(newUserEntity.getPhone()); + userEntity.setWebsite(newUserEntity.getWebsite()); + userEntity.setCompany(newUserEntity.getCompany()); + + return userRepository.save(userEntity); + + } + + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to update profile of: " + username); + throw new UnauthorizedException(apiResponse); + + } + + @Override + public ApiResponse deleteUser(String username, UserPrincipal currentUser) { + UserEntity userEntity = userRepository.findByUsername(username) + .orElseThrow(() -> new ResourceNotFoundException("User", "id", username)); + if (!userEntity.getId().equals(currentUser.getId()) || !currentUser.getAuthorities() + .contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to delete profile of: " + username); + throw new AccessDeniedException(apiResponse); + } + + userRepository.deleteById(userEntity.getId()); + + return new ApiResponse(Boolean.TRUE, "You successfully deleted profile of: " + username); + } + + @Override + public ApiResponse giveAdmin(String username) { + UserEntity userEntity = userRepository.getUserByName(username); + List roles = new ArrayList<>(); + roles.add(roleRepository.findByName(RoleName.ROLE_ADMIN) + .orElseThrow(() -> new AppException("User role not set"))); + roles.add( + roleRepository.findByName(RoleName.ROLE_USER).orElseThrow(() -> new AppException("User role not set"))); + userEntity.setRoles(roles); + userRepository.save(userEntity); + return new ApiResponse(Boolean.TRUE, "You gave ADMIN role to user: " + username); + } + + @Override + public ApiResponse removeAdmin(String username) { + UserEntity userEntity = userRepository.getUserByName(username); + List roles = new ArrayList<>(); + roles.add( + roleRepository.findByName(RoleName.ROLE_USER).orElseThrow(() -> new AppException("User role not set"))); + userEntity.setRoles(roles); + userRepository.save(userEntity); + return new ApiResponse(Boolean.TRUE, "You took ADMIN role from user: " + username); + } + + @Override + public UserProfile setOrUpdateInfo(UserPrincipal currentUser, InfoRequest infoRequest) { + UserEntity userEntity = userRepository.findByUsername(currentUser.getUsername()) + .orElseThrow(() -> new ResourceNotFoundException("User", "username", currentUser.getUsername())); + Geo geo = Geo.builder() + .lat(infoRequest.getLat()) + .lng(infoRequest.getLng()) + .build(); + Address address = Address.builder() + .street(infoRequest.getStreet()) + .suite(infoRequest.getSuite()) + .city(infoRequest.getCity()) + .zipcode(infoRequest.getZipcode()) + .geo(geo) + .build(); + Company company = Company.builder() + .name(infoRequest.getCompanyName()) + .catchPhrase(infoRequest.getCatchPhrase()) + .bs(infoRequest.getBs()) + .build(); + if (userEntity.getId().equals(currentUser.getId()) + || currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) { + userEntity.setAddress(address); + userEntity.setCompany(company); + userEntity.setWebsite(infoRequest.getWebsite()); + userEntity.setPhone(infoRequest.getPhone()); + UserEntity updatedUserEntity = userRepository.save(userEntity); + + Long postCount = postRepository.countByCreatedBy(updatedUserEntity.getId()); + + return new UserProfile(updatedUserEntity.getId(), updatedUserEntity.getUsername(), + updatedUserEntity.getFirstName(), updatedUserEntity.getLastName(), updatedUserEntity.getCreatedAt(), + updatedUserEntity.getEmail(), updatedUserEntity.getAddress(), updatedUserEntity.getPhone(), updatedUserEntity.getWebsite(), + updatedUserEntity.getCompany(), postCount); + } + + ApiResponse apiResponse = new ApiResponse(Boolean.FALSE, "You don't have permission to update users profile", HttpStatus.FORBIDDEN); + throw new AccessDeniedException(apiResponse); + } + } diff --git a/src/main/java/com/sopromadze/blogapi/utils/AppConstants.java b/src/main/java/com/sopromadze/blogapi/utils/AppConstants.java index 19df8f75..30544f36 100644 --- a/src/main/java/com/sopromadze/blogapi/utils/AppConstants.java +++ b/src/main/java/com/sopromadze/blogapi/utils/AppConstants.java @@ -1,31 +1,33 @@ package com.sopromadze.blogapi.utils; public class AppConstants { - public static final String DEFAULT_PAGE_NUMBER = "0"; - public static final String DEFAULT_PAGE_SIZE = "30"; + public static final String DEFAULT_PAGE_NUMBER = "0"; - public static final int MAX_PAGE_SIZE = 30; + public static final String DEFAULT_PAGE_SIZE = "30"; - public static final String CREATED_AT = "createdAt"; + public static final int MAX_PAGE_SIZE = 30; - public static final String ID = "id"; + public static final String CREATED_AT = "createdAt"; - public static final String PHOTO = "Photo"; + public static final String ID = "id"; - public static final String ALBUM = "Album"; + public static final String PHOTO = "Photo"; - public static final String USERNAME = "username"; + public static final String ALBUM = "Album"; - public static final String USER = "User"; + public static final String USERNAME = "username"; - public static final String CATEGORY = "Category"; + public static final String USER = "User"; - public static final String TAG = "Tag"; + public static final String CATEGORY = "Category"; - public static final String POST = "Post"; + public static final String TAG = "Tag"; - public static final String TODO = "ToDo"; + public static final String POST = "Post"; + + public static final String TODO = "ToDo"; + + public static final String YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION = "You don't have permission to make this operation"; - public static final String YOU_DON_T_HAVE_PERMISSION_TO_MAKE_THIS_OPERATION = "You don't have permission to make this operation"; } diff --git a/src/main/java/com/sopromadze/blogapi/utils/AppUtils.java b/src/main/java/com/sopromadze/blogapi/utils/AppUtils.java index fccdf832..90502bba 100644 --- a/src/main/java/com/sopromadze/blogapi/utils/AppUtils.java +++ b/src/main/java/com/sopromadze/blogapi/utils/AppUtils.java @@ -4,17 +4,19 @@ import org.springframework.http.HttpStatus; public class AppUtils { - public static void validatePageNumberAndSize(int page, int size) { - if (page < 0) { - throw new BlogapiException(HttpStatus.BAD_REQUEST, "Page number cannot be less than zero."); - } - if (size < 0) { - throw new BlogapiException(HttpStatus.BAD_REQUEST, "Size number cannot be less than zero."); - } + public static void validatePageNumberAndSize(int page, int size) { + if (page < 0) { + throw new BlogapiException(HttpStatus.BAD_REQUEST, "Page number cannot be less than zero."); + } + + if (size < 0) { + throw new BlogapiException(HttpStatus.BAD_REQUEST, "Size number cannot be less than zero."); + } + + if (size > AppConstants.MAX_PAGE_SIZE) { + throw new BlogapiException(HttpStatus.BAD_REQUEST, "Page size must not be greater than " + AppConstants.MAX_PAGE_SIZE); + } + } - if (size > AppConstants.MAX_PAGE_SIZE) { - throw new BlogapiException(HttpStatus.BAD_REQUEST, "Page size must not be greater than " + AppConstants.MAX_PAGE_SIZE); - } - } } diff --git a/src/test/java/com/sopromadze/blogapi/BlogApiApplicationTests.java b/src/test/java/com/sopromadze/blogapi/BlogApiApplicationTests.java deleted file mode 100644 index b1247c5c..00000000 --- a/src/test/java/com/sopromadze/blogapi/BlogApiApplicationTests.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.sopromadze.blogapi; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = BlogApiApplication.class) -public class BlogApiApplicationTests { - - @Test - public void contextLoads() { - } - -}