From 561ca3400f04dfd236ab02430bc4c1a388af88af Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Sun, 25 Aug 2024 18:15:38 +0400 Subject: [PATCH 01/15] Completed task 2 --- resources/view/login.html | 8 ----- resources/view/unauth/register.html | 8 ----- .../handler/VkOAuth2UserDataHandler.java | 35 ------------------- .../handler/YandexOAuth2UserDataHandler.java | 21 ----------- src/main/resources/application.yaml | 24 ------------- src/main/resources/data4dev/data.sql | 3 +- src/main/resources/db/changelog.sql | 1 - .../profile/internal/web/ProfileTestData.java | 2 -- src/test/resources/data.sql | 3 +- 9 files changed, 2 insertions(+), 103 deletions(-) delete mode 100644 src/main/java/com/javarush/jira/login/internal/sociallogin/handler/VkOAuth2UserDataHandler.java delete mode 100644 src/main/java/com/javarush/jira/login/internal/sociallogin/handler/YandexOAuth2UserDataHandler.java diff --git a/resources/view/login.html b/resources/view/login.html index 8765ca8ff..d49ce5691 100644 --- a/resources/view/login.html +++ b/resources/view/login.html @@ -48,14 +48,6 @@

Sign in

type="button"> - - - - - - diff --git a/resources/view/unauth/register.html b/resources/view/unauth/register.html index 2ba955045..52a892bd3 100644 --- a/resources/view/unauth/register.html +++ b/resources/view/unauth/register.html @@ -77,14 +77,6 @@

Registration

type="button"> - - - - - - diff --git a/src/main/java/com/javarush/jira/login/internal/sociallogin/handler/VkOAuth2UserDataHandler.java b/src/main/java/com/javarush/jira/login/internal/sociallogin/handler/VkOAuth2UserDataHandler.java deleted file mode 100644 index e8e05be05..000000000 --- a/src/main/java/com/javarush/jira/login/internal/sociallogin/handler/VkOAuth2UserDataHandler.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.javarush.jira.login.internal.sociallogin.handler; - -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.Map; - -@Component("vk") -public class VkOAuth2UserDataHandler implements OAuth2UserDataHandler { - @Override - public String getFirstName(OAuth2UserData oAuth2UserData) { - return getAttribute(oAuth2UserData, "first_name"); - } - - @Override - public String getLastName(OAuth2UserData oAuth2UserData) { - return getAttribute(oAuth2UserData, "last_name"); - } - - @Override - public String getEmail(OAuth2UserData oAuth2UserData) { - return oAuth2UserData.getData("email"); - } - - private String getAttribute(OAuth2UserData oAuth2UserData, String name) { - List> attributesResponse = oAuth2UserData.getData("response"); - if (attributesResponse != null) { - Map attributes = attributesResponse.get(0); - if (attributes != null) { - return (String) attributes.get(name); - } - } - return null; - } -} diff --git a/src/main/java/com/javarush/jira/login/internal/sociallogin/handler/YandexOAuth2UserDataHandler.java b/src/main/java/com/javarush/jira/login/internal/sociallogin/handler/YandexOAuth2UserDataHandler.java deleted file mode 100644 index e8ea1ac1d..000000000 --- a/src/main/java/com/javarush/jira/login/internal/sociallogin/handler/YandexOAuth2UserDataHandler.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.javarush.jira.login.internal.sociallogin.handler; - -import org.springframework.stereotype.Component; - -@Component("yandex") -public class YandexOAuth2UserDataHandler implements OAuth2UserDataHandler { - @Override - public String getFirstName(OAuth2UserData oAuth2UserData) { - return oAuth2UserData.getData("first_name"); - } - - @Override - public String getLastName(OAuth2UserData oAuth2UserData) { - return oAuth2UserData.getData("last_name"); - } - - @Override - public String getEmail(OAuth2UserData oAuth2UserData) { - return oAuth2UserData.getData("default_email"); - } -} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 7fcba1570..d9660eb05 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -61,20 +61,6 @@ spring: scope: - email - profile - vk: - client-id: 51562377 - client-secret: jNM1YHQy1362Mqs49wUN - client-name: Vkontakte - redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}" - client-authentication-method: client_secret_post - authorization-grant-type: authorization_code - scope: email - yandex: - client-id: 2f3395214ba84075956b76a34b231985 - client-secret: ed236c501e444a609b0f419e5e88f1e1 - client-name: Yandex - redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}" - authorization-grant-type: authorization_code gitlab: client-id: b8520a3266089063c0d8261cce36971defa513f5ffd9f9b7a3d16728fc83a494 client-secret: e72c65320cf9d6495984a37b0f9cc03ec46be0bb6f071feaebbfe75168117004 @@ -83,16 +69,6 @@ spring: authorization-grant-type: authorization_code scope: read_user provider: - vk: - authorization-uri: https://oauth.vk.com/authorize - token-uri: https://oauth.vk.com/access_token - user-info-uri: https://api.vk.com/method/users.get?v=8.1 - user-name-attribute: response - yandex: - authorization-uri: https://oauth.yandex.ru/authorize - token-uri: https://oauth.yandex.ru/token - user-info-uri: https://login.yandex.ru/info - user-name-attribute: login gitlab: authorization-uri: https://gitlab.com/oauth/authorize token-uri: https://gitlab.com/oauth/token diff --git a/src/main/resources/data4dev/data.sql b/src/main/resources/data4dev/data.sql index a7d43cbad..28b494155 100644 --- a/src/main/resources/data4dev/data.sql +++ b/src/main/resources/data4dev/data.sql @@ -54,8 +54,7 @@ values (1, 'skype', 'userSkype'), (1, 'mobile', '+01234567890'), (1, 'website', 'user.com'), (2, 'github', 'adminGitHub'), - (2, 'tg', 'adminTg'), - (2, 'vk', 'adminVk'); + (2, 'tg', 'adminTg'); delete from ATTACHMENT; diff --git a/src/main/resources/db/changelog.sql b/src/main/resources/db/changelog.sql index 68591336d..1595f97d3 100644 --- a/src/main/resources/db/changelog.sql +++ b/src/main/resources/db/changelog.sql @@ -218,7 +218,6 @@ values ('task', 'Task', 2), ('mobile', 'Mobile', 0), ('phone', 'Phone', 0), ('website', 'Website', 0), - ('vk', 'VK', 0), ('linkedin', 'LinkedIn', 0), ('github', 'GitHub', 0), -- PRIORITY diff --git a/src/test/java/com/javarush/jira/profile/internal/web/ProfileTestData.java b/src/test/java/com/javarush/jira/profile/internal/web/ProfileTestData.java index fb4407268..cc0513971 100644 --- a/src/test/java/com/javarush/jira/profile/internal/web/ProfileTestData.java +++ b/src/test/java/com/javarush/jira/profile/internal/web/ProfileTestData.java @@ -44,7 +44,6 @@ public static ProfileTo getUpdatedTo() { new ContactTo("website", "new.com"), new ContactTo("github", "newGitHub"), new ContactTo("tg", "newTg"), - new ContactTo("vk", "newVk"), new ContactTo("linkedin", "newLinkedin"))); } @@ -57,7 +56,6 @@ public static Profile getUpdated(long id) { new Contact(id, "website", "new.com"), new Contact(id, "github", "newGitHub"), new Contact(id, "tg", "newTg"), - new Contact(id, "vk", "newVk"), new Contact(id, "linkedin", "newLinkedin"))); return profile; } diff --git a/src/test/resources/data.sql b/src/test/resources/data.sql index 5087dbddc..bc26161e4 100644 --- a/src/test/resources/data.sql +++ b/src/test/resources/data.sql @@ -53,8 +53,7 @@ values (1, 'skype', 'userSkype'), (1, 'mobile', '+01234567890'), (1, 'website', 'user.com'), (2, 'github', 'adminGitHub'), - (2, 'tg', 'adminTg'), - (2, 'vk', 'adminVk'); + (2, 'tg', 'adminTg'); insert into PROJECT (code, title, description, type_code, parent_id) From a4a95a6f8dac99f10fcd60aec0a41a66042e2a06 Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Sun, 25 Aug 2024 20:58:52 +0400 Subject: [PATCH 02/15] Completed task 3. ADDED application-security.yaml --- src/main/resources/application-security.yaml | 30 ++++++++++++++++++++ src/main/resources/application.yaml | 23 +++------------ 2 files changed, 34 insertions(+), 19 deletions(-) create mode 100644 src/main/resources/application-security.yaml diff --git a/src/main/resources/application-security.yaml b/src/main/resources/application-security.yaml new file mode 100644 index 000000000..dc31d3c19 --- /dev/null +++ b/src/main/resources/application-security.yaml @@ -0,0 +1,30 @@ +spring: + datasource: + username: ${DATASOURCE_USERNAME} + password: ${DATASOURCE_PASSWORD} + + security: + oauth2: + client: + registration: + github: + client-id: ${OAUTH2_GITHUB_CLIENT_ID} + client-secret: ${OAUTH2_GITHUB_CLIENT_SECRET} + google: + client-id: ${OAUTH2_GOOGLE_CLIENT_ID} + client-secret: ${OAUTH2_GOOGLE_CLIENT_SECRET} + gitlab: + client-id: ${OAUTH2_GITLAB_CLIENT_ID} + client-secret: ${OAUTH2_GITLAB_CLIENT_SECRET} + + mail: + properties: + mail: + smtp: + starttls: + enable: true + auth: true + host: ${MAIL_HOST} + username: ${MAIL_USERNAME} + password: ${MAIL_PASSWORD} + port: ${MAIL_PORT} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index d9660eb05..dc652a002 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -10,6 +10,10 @@ app: spring: init: mode: never + + profiles: + include: 'security' + jpa: show-sql: true open-in-view: false @@ -27,8 +31,6 @@ spring: jdbc.batch_size: 20 datasource: url: jdbc:postgresql://localhost:5432/jira - username: jira - password: JiraRush liquibase: changeLog: "classpath:db/changelog.sql" @@ -51,19 +53,13 @@ spring: client: registration: github: - client-id: 3d0d8738e65881fff266 - client-secret: 0f97031ce6178b7dfb67a6af587f37e222a16120 scope: - email google: - client-id: 329113642700-f8if6pu68j2repq3ef6umd5jgiliup60.apps.googleusercontent.com - client-secret: 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 scope: - email - profile gitlab: - client-id: b8520a3266089063c0d8261cce36971defa513f5ffd9f9b7a3d16728fc83a494 - client-secret: e72c65320cf9d6495984a37b0f9cc03ec46be0bb6f071feaebbfe75168117004 client-name: GitLab redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}" authorization-grant-type: authorization_code @@ -79,17 +75,6 @@ spring: init: mode: always - mail: - properties: - mail: - smtp: - starttls: - enable: true - auth: true - host: smtp.gmail.com - username: jira4jr@gmail.com - password: zdfzsrqvgimldzyj - port: 587 thymeleaf.check-template-location: false mvc.throw-exception-if-no-handler-found: true From b3587be5d18ede69d6c07f3b4e858666330df787 Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Tue, 27 Aug 2024 18:40:09 +0400 Subject: [PATCH 03/15] Completed task 4. ADDED H2 db for test, profiles test-h2, test-postgres --- pom.xml | 20 ++ .../internal/config/DataSourceConfig.java | 44 +++ .../javarush/jira/AbstractControllerTest.java | 2 +- .../java/com/javarush/jira/BaseTests.java | 2 +- src/test/resources/application-test-h2.yaml | 11 + ...st.yaml => application-test-postgres.yaml} | 2 +- src/test/resources/data-h2.sql | 96 +++++ .../resources/{data.sql => data-postgres.sql} | 15 +- src/test/resources/db/changelog-test-h2.sql | 328 ++++++++++++++++++ 9 files changed, 510 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/javarush/jira/common/internal/config/DataSourceConfig.java create mode 100644 src/test/resources/application-test-h2.yaml rename src/test/resources/{application-test.yaml => application-test-postgres.yaml} (86%) create mode 100644 src/test/resources/data-h2.sql rename src/test/resources/{data.sql => data-postgres.sql} (91%) create mode 100644 src/test/resources/db/changelog-test-h2.sql diff --git a/pom.xml b/pom.xml index f6c152c68..3b6f08011 100644 --- a/pom.xml +++ b/pom.xml @@ -88,6 +88,12 @@ runtime + + com.h2database + h2 + test + + org.liquibase liquibase-core @@ -195,5 +201,19 @@ + + + test-h2 + + test-h2 + + + + + test-postgres + + test-postgres + + diff --git a/src/main/java/com/javarush/jira/common/internal/config/DataSourceConfig.java b/src/main/java/com/javarush/jira/common/internal/config/DataSourceConfig.java new file mode 100644 index 000000000..98165b8ca --- /dev/null +++ b/src/main/java/com/javarush/jira/common/internal/config/DataSourceConfig.java @@ -0,0 +1,44 @@ +package com.javarush.jira.common.internal.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +import javax.sql.DataSource; + +@Configuration +public class DataSourceConfig { + + @Value("${spring.datasource.url}") + private String url; + + @Value("${spring.datasource.username}") + private String username; + + @Value("${spring.datasource.password}") + private String password; + + @Bean + @Profile("test-postgres") + public DataSource postgresDataSource() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName("org.postgresql.Driver"); + dataSource.setUrl(url); + dataSource.setUsername(username); + dataSource.setPassword(password); + return dataSource; + } + + @Bean + @Profile("test-h2") + public DataSource h2DataSource() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName("org.h2.Driver"); + dataSource.setUrl(url); + dataSource.setUsername(username); + dataSource.setPassword(password); + return dataSource; + } +} diff --git a/src/test/java/com/javarush/jira/AbstractControllerTest.java b/src/test/java/com/javarush/jira/AbstractControllerTest.java index 5981bae53..393cb93a3 100644 --- a/src/test/java/com/javarush/jira/AbstractControllerTest.java +++ b/src/test/java/com/javarush/jira/AbstractControllerTest.java @@ -9,7 +9,7 @@ import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; //https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-testing-spring-boot-applications -@Sql(scripts = {"classpath:db/changelog.sql", "classpath:data.sql"}, config = @SqlConfig(encoding = "UTF-8")) +@Sql(scripts = {"classpath:db/changelog-test-h2.sql", "classpath:data-h2.sql"}, config = @SqlConfig(encoding = "UTF-8")) @AutoConfigureMockMvc //https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-mock-environment public abstract class AbstractControllerTest extends BaseTests { diff --git a/src/test/java/com/javarush/jira/BaseTests.java b/src/test/java/com/javarush/jira/BaseTests.java index b6ed2d1aa..040c81b7b 100644 --- a/src/test/java/com/javarush/jira/BaseTests.java +++ b/src/test/java/com/javarush/jira/BaseTests.java @@ -4,6 +4,6 @@ import org.springframework.test.context.ActiveProfiles; @SpringBootTest -@ActiveProfiles("test") +@ActiveProfiles("test-h2") abstract class BaseTests { } diff --git a/src/test/resources/application-test-h2.yaml b/src/test/resources/application-test-h2.yaml new file mode 100644 index 000000000..b9f7c8df4 --- /dev/null +++ b/src/test/resources/application-test-h2.yaml @@ -0,0 +1,11 @@ +spring.cache.type: none +spring: + init: + mode: always + datasource: + url: jdbc:h2:mem:jira-test;DB_CLOSE_DELAY=-1 + username: jira + password: JiraRush + + liquibase: + change-log: db/changelog-test-h2.sql \ No newline at end of file diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test-postgres.yaml similarity index 86% rename from src/test/resources/application-test.yaml rename to src/test/resources/application-test-postgres.yaml index 51137fd06..612196843 100644 --- a/src/test/resources/application-test.yaml +++ b/src/test/resources/application-test-postgres.yaml @@ -5,4 +5,4 @@ spring: datasource: url: jdbc:postgresql://localhost:5433/jira-test username: jira - password: JiraRush \ No newline at end of file + password: JiraRush diff --git a/src/test/resources/data-h2.sql b/src/test/resources/data-h2.sql new file mode 100644 index 000000000..d71bfffca --- /dev/null +++ b/src/test/resources/data-h2.sql @@ -0,0 +1,96 @@ +--------- users ---------------------- +delete +from USER_ROLE; +delete +from CONTACT; +delete +from PROFILE; + +delete +from ACTIVITY; +alter table ACTIVITY alter column ID restart with 1; + +delete +from TASK; +alter table TASK alter column ID restart with 1; + +delete +from SPRINT; +alter table SPRINT alter column ID restart with 1; + +delete +from PROJECT; +alter table PROJECT alter column ID restart with 1; + +delete +from USERS; +alter table USERS alter column ID restart with 1; + +insert into USERS (EMAIL, PASSWORD, FIRST_NAME, LAST_NAME, DISPLAY_NAME) +values ('user@gmail.com', '{noop}password', 'userFirstName', 'userLastName', 'userDisplayName'), + ('admin@gmail.com', '{noop}admin', 'adminFirstName', 'adminLastName', 'adminDisplayName'), + ('guest@gmail.com', '{noop}guest', 'guestFirstName', 'guestLastName', 'guestDisplayName'), + ('manager@gmail.com', '{noop}manager', 'managerFirstName', 'managerLastName', 'managerDisplayName'); + +-- 0 DEV +-- 1 ADMIN +-- 2 MANAGER + +insert into USER_ROLE (USER_ID, ROLE) +values (1, 0), + (2, 0), + (2, 1), + (4, 2); + +insert into PROFILE (ID, LAST_FAILED_LOGIN, LAST_LOGIN, MAIL_NOTIFICATIONS) +values (1, null, null, 49), + (2, null, null, 14); + +insert into CONTACT (ID, CODE, "VALUE") +values (1, 'skype', 'userSkype'), + (1, 'mobile', '+01234567890'), + (1, 'website', 'user.com'), + (2, 'github', 'adminGitHub'), + (2, 'tg', 'adminTg'); + + +insert into PROJECT (CODE, TITLE, DESCRIPTION, TYPE_CODE, PARENT_ID) +values ('PR1', 'PROJECT-1', 'test project 1', 'task_tracker', null), + ('PR2', 'PROJECT-2', 'test project 2', 'task_tracker', 1); + +insert into SPRINT (STATUS_CODE, STARTPOINT, ENDPOINT, CODE, PROJECT_ID) +values ('finished', '2023-05-01 08:05:10', '2023-05-07 17:10:01', 'SP-1.001', 1), + ('active', '2023-05-01 08:06:00', null, 'SP-1.002', 1), + ('active', '2023-05-01 08:07:00', null, 'SP-1.003', 1), + ('planning', '2023-05-01 08:08:00', null, 'SP-1.004', 1), + ('active', '2023-05-10 08:06:00', null, 'SP-2.001', 2), + ('planning', '2023-05-10 08:07:00', null, 'SP-2.002', 2), + ('planning', '2023-05-10 08:08:00', null, 'SP-2.003', 2); + +insert into TASK (TITLE, TYPE_CODE, STATUS_CODE, PROJECT_ID, SPRINT_ID, STARTPOINT) +values ('Data', 'epic', 'in_progress', 1, 1, '2023-05-15 09:05:10'), + ('Trees', 'epic', 'in_progress', 1, 1, '2023-05-15 12:05:10'), + ('task-3', 'task', 'ready_for_test', 2, 5, '2023-06-14 09:28:10'), + ('task-4', 'task', 'ready_for_review', 2, 5, '2023-06-14 09:28:10'), + ('task-5', 'task', 'todo', 2, 5, '2023-06-14 09:28:10'), + ('task-6', 'task', 'done', 2, 5, '2023-06-14 09:28:10'), + ('task-7', 'task', 'canceled', 2, 5, '2023-06-14 09:28:10'); + + +insert into ACTIVITY (AUTHOR_ID, TASK_ID, UPDATED, COMMENT, TITLE, DESCRIPTION, ESTIMATE, TYPE_CODE, STATUS_CODE, + PRIORITY_CODE) +values (1, 1, '2023-05-15 09:05:10', null, 'Data', null, 3, 'epic', 'in_progress', 'low'), + (2, 1, '2023-05-15 12:25:10', null, 'Data', null, null, null, null, 'normal'), + (1, 1, '2023-05-15 14:05:10', null, 'Data', null, 4, null, null, null), + (1, 2, '2023-05-15 12:05:10', null, 'Trees', 'Trees desc', 4, 'epic', 'in_progress', 'normal'); + + +insert into USER_BELONG (OBJECT_ID, OBJECT_TYPE, USER_ID, USER_TYPE_CODE, STARTPOINT, ENDPOINT) +values (1, 2, 2, 'task_developer', '2023-06-14 08:35:10', '2023-06-14 08:55:00'), + (1, 2, 2, 'task_reviewer', '2023-06-14 09:35:10', null), + (1, 2, 1, 'task_developer', '2023-06-12 11:40:00', '2023-06-12 12:35:00'), + (1, 2, 1, 'task_developer', '2023-06-13 12:35:00', null), + (1, 2, 1, 'task_tester', '2023-06-14 15:20:00', null), + (2, 2, 2, 'task_developer', '2023-06-08 07:10:00', null), + (2, 2, 1, 'task_developer', '2023-06-09 14:48:00', null), + (2, 2, 1, 'task_tester', '2023-06-10 16:37:00', null); diff --git a/src/test/resources/data.sql b/src/test/resources/data-postgres.sql similarity index 91% rename from src/test/resources/data.sql rename to src/test/resources/data-postgres.sql index bc26161e4..ba459710c 100644 --- a/src/test/resources/data.sql +++ b/src/test/resources/data-postgres.sql @@ -9,24 +9,24 @@ from PROFILE; delete from ACTIVITY; alter -sequence ACTIVITY_ID_SEQ restart with 1; + sequence ACTIVITY_ID_SEQ restart with 1; delete from TASK; alter -sequence TASK_ID_SEQ restart with 1; + sequence TASK_ID_SEQ restart with 1; delete from SPRINT; alter -sequence SPRINT_ID_SEQ restart with 1; + sequence SPRINT_ID_SEQ restart with 1; delete from PROJECT; alter -sequence PROJECT_ID_SEQ restart with 1; + sequence PROJECT_ID_SEQ restart with 1; delete from USERS; alter -sequence USERS_ID_SEQ restart with 1; + sequence USERS_ID_SEQ restart with 1; insert into USERS (EMAIL, PASSWORD, FIRST_NAME, LAST_NAME, DISPLAY_NAME) values ('user@gmail.com', '{noop}password', 'userFirstName', 'userLastName', 'userDisplayName'), @@ -53,7 +53,8 @@ values (1, 'skype', 'userSkype'), (1, 'mobile', '+01234567890'), (1, 'website', 'user.com'), (2, 'github', 'adminGitHub'), - (2, 'tg', 'adminTg'); + (2, 'tg', 'adminTg'), + (2, 'vk', 'adminVk'); insert into PROJECT (code, title, description, type_code, parent_id) @@ -94,4 +95,4 @@ values (1, 2, 2, 'task_developer', '2023-06-14 08:35:10', '2023-06-14 08:55:00') (1, 2, 1, 'task_tester', '2023-06-14 15:20:00', null), (2, 2, 2, 'task_developer', '2023-06-08 07:10:00', null), (2, 2, 1, 'task_developer', '2023-06-09 14:48:00', null), - (2, 2, 1, 'task_tester', '2023-06-10 16:37:00', null); + (2, 2, 1, 'task_tester', '2023-06-10 16:37:00', null); \ No newline at end of file diff --git a/src/test/resources/db/changelog-test-h2.sql b/src/test/resources/db/changelog-test-h2.sql new file mode 100644 index 000000000..89e3d76a7 --- /dev/null +++ b/src/test/resources/db/changelog-test-h2.sql @@ -0,0 +1,328 @@ +--liquibase formatted sql + +--changeset kmpk:init_schema +DROP TABLE IF EXISTS USER_ROLE; +DROP TABLE IF EXISTS CONTACT; +DROP TABLE IF EXISTS MAIL_CASE; +DROP +SEQUENCE IF EXISTS MAIL_CASE_ID_SEQ; +DROP TABLE IF EXISTS PROFILE; +DROP TABLE IF EXISTS TASK_TAG; +DROP TABLE IF EXISTS USER_BELONG; +DROP +SEQUENCE IF EXISTS USER_BELONG_ID_SEQ; +DROP TABLE IF EXISTS ACTIVITY; +DROP +SEQUENCE IF EXISTS ACTIVITY_ID_SEQ; +DROP TABLE IF EXISTS TASK; +DROP +SEQUENCE IF EXISTS TASK_ID_SEQ; +DROP TABLE IF EXISTS SPRINT; +DROP +SEQUENCE IF EXISTS SPRINT_ID_SEQ; +DROP TABLE IF EXISTS PROJECT; +DROP +SEQUENCE IF EXISTS PROJECT_ID_SEQ; +DROP TABLE IF EXISTS REFERENCE; +DROP +SEQUENCE IF EXISTS REFERENCE_ID_SEQ; +DROP TABLE IF EXISTS ATTACHMENT; +DROP +SEQUENCE IF EXISTS ATTACHMENT_ID_SEQ; +DROP TABLE IF EXISTS USERS; +DROP +SEQUENCE IF EXISTS USERS_ID_SEQ; + +create table PROJECT +( + ID bigserial primary key, + CODE varchar(32) not null + constraint UK_PROJECT_CODE unique, + TITLE varchar(1024) not null, + DESCRIPTION varchar(4096) not null, + TYPE_CODE varchar(32) not null, + STARTPOINT timestamp, + ENDPOINT timestamp, + PARENT_ID bigint, + constraint FK_PROJECT_PARENT foreign key (PARENT_ID) references PROJECT (ID) on delete cascade +); + +create table MAIL_CASE +( + ID bigserial primary key, + EMAIL varchar(255) not null, + NAME varchar(255) not null, + DATE_TIME timestamp not null, + RESULT varchar(255) not null, + TEMPLATE varchar(255) not null +); + +create table SPRINT +( + ID bigserial primary key, + STATUS_CODE varchar(32) not null, + STARTPOINT timestamp, + ENDPOINT timestamp, + TITLE varchar(1024) not null, + PROJECT_ID bigint not null, + constraint FK_SPRINT_PROJECT foreign key (PROJECT_ID) references PROJECT (ID) on delete cascade +); + +create table REFERENCE +( + ID bigserial primary key, + CODE varchar(32) not null, + REF_TYPE smallint not null, + ENDPOINT timestamp, + STARTPOINT timestamp, + TITLE varchar(1024) not null, + AUX varchar, + constraint UK_REFERENCE_REF_TYPE_CODE unique (REF_TYPE, CODE) +); + +create table USERS +( + ID bigserial primary key, + DISPLAY_NAME varchar(32) not null + constraint UK_USERS_DISPLAY_NAME unique, + EMAIL varchar(128) not null + constraint UK_USERS_EMAIL unique, + FIRST_NAME varchar(32) not null, + LAST_NAME varchar(32), + PASSWORD varchar(128) not null, + ENDPOINT timestamp, + STARTPOINT timestamp +); + +create table PROFILE +( + ID bigint primary key, + LAST_LOGIN timestamp, + LAST_FAILED_LOGIN timestamp, + MAIL_NOTIFICATIONS bigint, + constraint FK_PROFILE_USERS foreign key (ID) references USERS (ID) on delete cascade +); + +create table CONTACT +( + ID bigint not null, + CODE varchar(32) not null, + "VALUE" varchar(256) not null, + primary key (ID, CODE), + constraint FK_CONTACT_PROFILE foreign key (ID) references PROFILE (ID) on delete cascade +); + +create table TASK +( + ID bigserial primary key, + TITLE varchar(1024) not null, + DESCRIPTION varchar(4096) not null, + TYPE_CODE varchar(32) not null, + STATUS_CODE varchar(32) not null, + PRIORITY_CODE varchar(32) not null, + ESTIMATE integer, + UPDATED timestamp, + PROJECT_ID bigint not null, + SPRINT_ID bigint, + PARENT_ID bigint, + STARTPOINT timestamp, + ENDPOINT timestamp, + constraint FK_TASK_SPRINT foreign key (SPRINT_ID) references SPRINT (ID) on delete set null, + constraint FK_TASK_PROJECT foreign key (PROJECT_ID) references PROJECT (ID) on delete cascade, + constraint FK_TASK_PARENT_TASK foreign key (PARENT_ID) references TASK (ID) on delete cascade +); + +create table ACTIVITY +( + ID bigserial primary key, + AUTHOR_ID bigint not null, + TASK_ID bigint not null, + UPDATED timestamp, + COMMENT varchar(4096), +-- history of task field change + TITLE varchar(1024), + DESCRIPTION varchar(4096), + ESTIMATE integer, + TYPE_CODE varchar(32), + STATUS_CODE varchar(32), + PRIORITY_CODE varchar(32), + constraint FK_ACTIVITY_USERS foreign key (AUTHOR_ID) references USERS (ID), + constraint FK_ACTIVITY_TASK foreign key (TASK_ID) references TASK (ID) on delete cascade +); + +create table TASK_TAG +( + TASK_ID bigint not null, + TAG varchar(32) not null, + constraint UK_TASK_TAG unique (TASK_ID, TAG), + constraint FK_TASK_TAG foreign key (TASK_ID) references TASK (ID) on delete cascade +); + +create table USER_BELONG +( + ID bigserial primary key, + OBJECT_ID bigint not null, + OBJECT_TYPE smallint not null, + USER_ID bigint not null, + USER_TYPE_CODE varchar(32) not null, + STARTPOINT timestamp, + ENDPOINT timestamp, + constraint FK_USER_BELONG foreign key (USER_ID) references USERS (ID) +); +-- create unique index UK_USER_BELONG on USER_BELONG (OBJECT_ID, OBJECT_TYPE, USER_ID, USER_TYPE_CODE); +create index IX_USER_BELONG_USER_ID on USER_BELONG (USER_ID); + +create table ATTACHMENT +( + ID bigserial primary key, + NAME varchar(128) not null, + FILE_LINK varchar(2048) not null, + OBJECT_ID bigint not null, + OBJECT_TYPE smallint not null, + USER_ID bigint not null, + DATE_TIME timestamp, + constraint FK_ATTACHMENT foreign key (USER_ID) references USERS (ID) +); + +create table USER_ROLE +( + USER_ID bigint not null, + ROLE smallint not null, + constraint UK_USER_ROLE unique (USER_ID, ROLE), + constraint FK_USER_ROLE foreign key (USER_ID) references USERS (ID) on delete cascade +); + +--changeset kmpk:populate_data +--============ References ================= +insert into REFERENCE (CODE, TITLE, REF_TYPE) +-- TASK +values ('task', 'Task', 2), + ('story', 'Story', 2), + ('bug', 'Bug', 2), + ('epic', 'Epic', 2), +-- SPRINT_STATUS + ('planning', 'Planning', 4), + ('active', 'Active', 4), + ('finished', 'Finished', 4), +-- USER_TYPE + ('author', 'Author', 5), + ('developer', 'Developer', 5), + ('reviewer', 'Reviewer', 5), + ('tester', 'Tester', 5), +-- PROJECT + ('scrum', 'Scrum', 1), + ('task_tracker', 'Task tracker', 1), +-- CONTACT + ('skype', 'Skype', 0), + ('tg', 'Telegram', 0), + ('mobile', 'Mobile', 0), + ('phone', 'Phone', 0), + ('website', 'Website', 0), + ('linkedin', 'LinkedIn', 0), + ('github', 'GitHub', 0), +-- PRIORITY + ('critical', 'Critical', 7), + ('high', 'High', 7), + ('normal', 'Normal', 7), + ('low', 'Low', 7), + ('neutral', 'Neutral', 7); + +insert into REFERENCE (CODE, TITLE, REF_TYPE, AUX) +-- MAIL_NOTIFICATION +values ('assigned', 'Assigned', 6, '1'), + ('three_days_before_deadline', 'Three days before deadline', 6, '2'), + ('two_days_before_deadline', 'Two days before deadline', 6, '4'), + ('one_day_before_deadline', 'One day before deadline', 6, '8'), + ('deadline', 'Deadline', 6, '16'), + ('overdue', 'Overdue', 6, '32'), +-- TASK_STATUS + ('todo', 'ToDo', 3, 'in_progress,canceled'), + ('in_progress', 'In progress', 3, 'ready_for_review,canceled'), + ('ready_for_review', 'Ready for review', 3, 'review,canceled'), + ('review', 'Review', 3, 'in_progress,ready_for_test,canceled'), + ('ready_for_test', 'Ready for test', 3, 'test,canceled'), + ('test', 'Test', 3, 'done,in_progress,canceled'), + ('done', 'Done', 3, 'canceled'), + ('canceled', 'Canceled', 3, null); + +--changeset gkislin:change_backtracking_tables + +alter table SPRINT rename COLUMN TITLE to CODE; +alter table SPRINT + alter column CODE type varchar (32); +alter table SPRINT + alter column CODE set not null; +create unique index UK_SPRINT_PROJECT_CODE on SPRINT (PROJECT_ID, CODE); + +ALTER TABLE TASK + DROP COLUMN DESCRIPTION; +ALTER TABLE TASK + DROP COLUMN PRIORITY_CODE; +ALTER TABLE TASK + DROP COLUMN ESTIMATE; +ALTER TABLE TASK + DROP COLUMN UPDATED; + +--changeset ishlyakhtenkov:change_task_status_reference + +delete +from REFERENCE +where REF_TYPE = 3; +insert into REFERENCE (CODE, TITLE, REF_TYPE, AUX) +values ('todo', 'ToDo', 3, 'in_progress,canceled'), + ('in_progress', 'In progress', 3, 'ready_for_review,canceled'), + ('ready_for_review', 'Ready for review', 3, 'in_progress,review,canceled'), + ('review', 'Review', 3, 'in_progress,ready_for_test,canceled'), + ('ready_for_test', 'Ready for test', 3, 'review,test,canceled'), + ('test', 'Test', 3, 'done,in_progress,canceled'), + ('done', 'Done', 3, 'canceled'), + ('canceled', 'Canceled', 3, null); + +--changeset gkislin:users_add_on_delete_cascade + +alter table ACTIVITY + drop constraint FK_ACTIVITY_USERS; +alter table ACTIVITY + add constraint FK_ACTIVITY_USERS foreign key (AUTHOR_ID) references USERS (ID) on delete cascade; + +alter table USER_BELONG + drop constraint FK_USER_BELONG; +alter table USER_BELONG + add constraint FK_USER_BELONG foreign key (USER_ID) references USERS (ID) on delete cascade; + +alter table ATTACHMENT + drop constraint FK_ATTACHMENT; +alter table ATTACHMENT + add constraint FK_ATTACHMENT foreign key (USER_ID) references USERS (ID) on delete cascade; + +--changeset valeriyemelyanov:change_user_type_reference + +delete +from REFERENCE +where REF_TYPE = 5; +insert into REFERENCE (CODE, TITLE, REF_TYPE) +-- USER_TYPE +values ('project_author', 'Author', 5), + ('project_manager', 'Manager', 5), + ('sprint_author', 'Author', 5), + ('sprint_manager', 'Manager', 5), + ('task_author', 'Author', 5), + ('task_developer', 'Developer', 5), + ('task_reviewer', 'Reviewer', 5), + ('task_tester', 'Tester', 5); + +--changeset apolik:refactor_reference_aux + +-- TASK_TYPE +delete +from REFERENCE +where REF_TYPE = 3; +insert into REFERENCE (CODE, TITLE, REF_TYPE, AUX) +values ('todo', 'ToDo', 3, 'in_progress,canceled|'), + ('in_progress', 'In progress', 3, 'ready_for_review,canceled|task_developer'), + ('ready_for_review', 'Ready for review', 3, 'in_progress,review,canceled|'), + ('review', 'Review', 3, 'in_progress,ready_for_test,canceled|task_reviewer'), + ('ready_for_test', 'Ready for test', 3, 'review,test,canceled|'), + ('test', 'Test', 3, 'done,in_progress,canceled|task_tester'), + ('done', 'Done', 3, 'canceled|'), + ('canceled', 'Canceled', 3, null); From e98649e7562579a748fba28d230414e517d16f85 Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Fri, 30 Aug 2024 16:23:35 +0400 Subject: [PATCH 04/15] Completed task 5. ADDED tests for ProfileRestController. FIX auto quote keyword --- src/main/resources/application.yaml | 1 + .../web/ProfileRestControllerTest.java | 90 +++++++++++++++++++ src/test/resources/data-h2.sql | 2 +- src/test/resources/db/changelog-test-h2.sql | 2 +- 4 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index dc652a002..6391da91b 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -29,6 +29,7 @@ spring: default_batch_fetch_size: 20 # https://stackoverflow.com/questions/21257819/what-is-the-difference-between-hibernate-jdbc-fetch-size-and-hibernate-jdbc-batc jdbc.batch_size: 20 + auto_quote_keyword: true datasource: url: jdbc:postgresql://localhost:5432/jira diff --git a/src/test/java/com/javarush/jira/profile/internal/web/ProfileRestControllerTest.java b/src/test/java/com/javarush/jira/profile/internal/web/ProfileRestControllerTest.java index a6fd5e3bf..99480221c 100644 --- a/src/test/java/com/javarush/jira/profile/internal/web/ProfileRestControllerTest.java +++ b/src/test/java/com/javarush/jira/profile/internal/web/ProfileRestControllerTest.java @@ -1,8 +1,98 @@ package com.javarush.jira.profile.internal.web; import com.javarush.jira.AbstractControllerTest; +import com.javarush.jira.common.BaseHandler; +import com.javarush.jira.common.util.JsonUtil; +import com.javarush.jira.profile.ProfileTo; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.security.test.context.support.WithUserDetails; +import org.springframework.test.context.TestConstructor; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import static com.javarush.jira.login.internal.web.UserTestData.USER_MAIL; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +@RequiredArgsConstructor +@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) class ProfileRestControllerTest extends AbstractControllerTest { + private static final String REST_URL_PROFILE = BaseHandler.REST_URL + "/profile"; + @Test + @WithUserDetails(USER_MAIL) + void whenGetWithAuthorizedUser_thenStatusIsOk() throws Exception { + perform(MockMvcRequestBuilders.get(REST_URL_PROFILE)) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)); + } + + @Test + void whenGetWithUnauthorizedUser_thenStatusIsUnauthorized() throws Exception { + perform(MockMvcRequestBuilders.get(REST_URL_PROFILE)) + .andExpect(status().isUnauthorized()) + .andDo(print()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)); + } + + @Test + @WithUserDetails(USER_MAIL) + void whenUpdateWithAuthUserAndCorrectData_thenStatusIsNoContent() throws Exception { + ProfileTo updatedTo = ProfileTestData.getUpdatedTo(); + String json = JsonUtil.writeValue(updatedTo); + perform(MockMvcRequestBuilders.put(REST_URL_PROFILE).contentType(APPLICATION_JSON_VALUE).content(json)) + .andExpect(status().isNoContent()) + .andDo(print()); + } + + @Test + void whenUpdateWithUnauthorizedUser_thenStatusIsUnauthorized() throws Exception { + perform(MockMvcRequestBuilders.put(REST_URL_PROFILE).contentType(APPLICATION_JSON_VALUE)) + .andExpect(status().isUnauthorized()) + .andDo(print()); + } + + @Test + @WithUserDetails(USER_MAIL) + void whenUpdateWithInvalidTo_thenStatusIs4xx() throws Exception { + ProfileTo invalidTo = ProfileTestData.getInvalidTo(); + String json = JsonUtil.writeValue(invalidTo); + perform(MockMvcRequestBuilders.put(REST_URL_PROFILE).contentType(APPLICATION_JSON_VALUE).content(json)) + .andExpect(status().is4xxClientError()) + .andDo(print()); + } + + @Test + @WithUserDetails(USER_MAIL) + void whenUpdateWithUnknownNotificationTo_thenStatusIs4xx() throws Exception { + ProfileTo profileTo = ProfileTestData.getWithUnknownNotificationTo(); + String json = JsonUtil.writeValue(profileTo); + perform(MockMvcRequestBuilders.put(REST_URL_PROFILE).contentType(APPLICATION_JSON_VALUE).content(json)) + .andExpect(status().is4xxClientError()) + .andDo(print()); + } + + @Test + @WithUserDetails(USER_MAIL) + void whenUpdateWithUnknownContactTo_thenStatusIs4xx() throws Exception { + ProfileTo profileTo = ProfileTestData.getWithUnknownContactTo(); + String json = JsonUtil.writeValue(profileTo); + perform(MockMvcRequestBuilders.put(REST_URL_PROFILE).contentType(APPLICATION_JSON_VALUE).content(json)) + .andExpect(status().is4xxClientError()) + .andDo(print()); + } + + @Test + @WithUserDetails(USER_MAIL) + void whenUpdateWithContactHtmlUnsafeTo_thenStatusIs4xx() throws Exception { + ProfileTo profileTo = ProfileTestData.getWithContactHtmlUnsafeTo(); + String json = JsonUtil.writeValue(profileTo); + perform(MockMvcRequestBuilders.put(REST_URL_PROFILE).contentType(APPLICATION_JSON_VALUE).content(json)) + .andExpect(status().is4xxClientError()) + .andDo(print()); + } } \ No newline at end of file diff --git a/src/test/resources/data-h2.sql b/src/test/resources/data-h2.sql index d71bfffca..edf30d9a6 100644 --- a/src/test/resources/data-h2.sql +++ b/src/test/resources/data-h2.sql @@ -46,7 +46,7 @@ insert into PROFILE (ID, LAST_FAILED_LOGIN, LAST_LOGIN, MAIL_NOTIFICATIONS) values (1, null, null, 49), (2, null, null, 14); -insert into CONTACT (ID, CODE, "VALUE") +insert into CONTACT (ID, CODE, "value") values (1, 'skype', 'userSkype'), (1, 'mobile', '+01234567890'), (1, 'website', 'user.com'), diff --git a/src/test/resources/db/changelog-test-h2.sql b/src/test/resources/db/changelog-test-h2.sql index 89e3d76a7..f26f5d64f 100644 --- a/src/test/resources/db/changelog-test-h2.sql +++ b/src/test/resources/db/changelog-test-h2.sql @@ -107,7 +107,7 @@ create table CONTACT ( ID bigint not null, CODE varchar(32) not null, - "VALUE" varchar(256) not null, + "value" varchar(256) not null, primary key (ID, CODE), constraint FK_CONTACT_PROFILE foreign key (ID) references PROFILE (ID) on delete cascade ); From 32abf1987c2c187971a63b9cc7915eb8152200b7 Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Fri, 30 Aug 2024 17:01:39 +0400 Subject: [PATCH 05/15] Completed task 6. Refactoring FileUtil.upload --- .../jira/bugtracking/attachment/FileUtil.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/javarush/jira/bugtracking/attachment/FileUtil.java b/src/main/java/com/javarush/jira/bugtracking/attachment/FileUtil.java index 6cffbe175..dac5d7597 100644 --- a/src/main/java/com/javarush/jira/bugtracking/attachment/FileUtil.java +++ b/src/main/java/com/javarush/jira/bugtracking/attachment/FileUtil.java @@ -7,10 +7,7 @@ import org.springframework.core.io.UrlResource; import org.springframework.web.multipart.MultipartFile; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.net.MalformedURLException; import java.nio.file.Files; import java.nio.file.Path; @@ -25,14 +22,13 @@ public static void upload(MultipartFile multipartFile, String directoryPath, Str throw new IllegalRequestDataException("Select a file to upload."); } - File dir = new File(directoryPath); - if (dir.exists() || dir.mkdirs()) { - File file = new File(directoryPath + fileName); - try (OutputStream outStream = new FileOutputStream(file)) { - outStream.write(multipartFile.getBytes()); - } catch (IOException ex) { - throw new IllegalRequestDataException("Failed to upload file" + multipartFile.getOriginalFilename()); - } + Path path = Paths.get(directoryPath); + try { + Files.createDirectories(path); + Path file = path.resolve(fileName); + multipartFile.transferTo(file); + } catch (IOException e) { + throw new IllegalRequestDataException("Failed to upload file" + multipartFile.getOriginalFilename()); } } From 18764ab0617e1fe0023dc08e5ec47d5f40f04a31 Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Sun, 1 Sep 2024 16:10:47 +0400 Subject: [PATCH 06/15] Completed task 7. ADDED change tags --- resources/view/task.html | 1 + .../jira/bugtracking/task/TaskController.java | 8 +++++ .../jira/bugtracking/task/TaskRepository.java | 4 +-- .../jira/bugtracking/task/TaskService.java | 9 ++++++ .../bugtracking/task/TaskUIController.java | 1 + .../jira/bugtracking/task/to/TaskTo.java | 12 +++++++- .../jira/bugtracking/task/to/TaskToExt.java | 11 ++++--- .../jira/bugtracking/task/to/TaskToFull.java | 8 +++-- .../bugtracking/task/TaskControllerTest.java | 30 ++++++++++++++----- .../jira/bugtracking/task/TaskTestData.java | 15 ++++++---- 10 files changed, 75 insertions(+), 24 deletions(-) diff --git a/resources/view/task.html b/resources/view/task.html index 697934470..3a012e6ef 100644 --- a/resources/view/task.html +++ b/resources/view/task.html @@ -13,6 +13,7 @@
Status: [[${task.statusCode}]]
Type: [[${task.typeCode}]]
Updated: [[${#temporals.format(task?.updated, 'MM/dd/yyyy, HH:mm:ss')} ?: '-' ]]
Estimate: [[${task.estimate} ?: '-']]
+
Tags:

Parent: [[${task.parent.code}]]
diff --git a/src/main/java/com/javarush/jira/bugtracking/task/TaskController.java b/src/main/java/com/javarush/jira/bugtracking/task/TaskController.java index b53f7ff37..9bbd95143 100644 --- a/src/main/java/com/javarush/jira/bugtracking/task/TaskController.java +++ b/src/main/java/com/javarush/jira/bugtracking/task/TaskController.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.Set; import static com.javarush.jira.common.BaseHandler.createdResponse; @@ -107,6 +108,13 @@ public void changeTaskSprint(@PathVariable long id, @Nullable @RequestParam Long taskService.changeSprint(id, sprintId); } + @PatchMapping("/{id}/change-tags") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void changeTaskTags(@PathVariable long id, @Nullable @RequestParam Set tags) { + log.info("change task(id={}) tags to {}", id, tags); + taskService.changeTags(id, tags); + } + @GetMapping("/assignments/by-sprint") public List getTaskAssignmentsBySprint(@RequestParam long sprintId) { log.info("get task assignments for user {} for sprint {}", AuthUser.authId(), sprintId); diff --git a/src/main/java/com/javarush/jira/bugtracking/task/TaskRepository.java b/src/main/java/com/javarush/jira/bugtracking/task/TaskRepository.java index 982c4be8b..a3b782299 100644 --- a/src/main/java/com/javarush/jira/bugtracking/task/TaskRepository.java +++ b/src/main/java/com/javarush/jira/bugtracking/task/TaskRepository.java @@ -10,13 +10,13 @@ @Transactional(readOnly = true) public interface TaskRepository extends BaseRepository { - @Query("SELECT t FROM Task t WHERE t.sprintId =:sprintId ORDER BY t.startpoint DESC") + @Query("SELECT DISTINCT t FROM Task t LEFT JOIN FETCH t.tags WHERE t.sprintId =:sprintId ORDER BY t.startpoint DESC") List findAllBySprintId(long sprintId); @Query("SELECT t FROM Task t WHERE t.projectId =:projectId AND t.sprintId IS NULL") List findAllByProjectIdAndSprintIsNull(long projectId); - @Query("SELECT t FROM Task t WHERE t.projectId =:projectId ORDER BY t.startpoint DESC") + @Query("SELECT DISTINCT t FROM Task t LEFT JOIN FETCH t.tags WHERE t.projectId =:projectId ORDER BY t.startpoint DESC") List findAllByProjectId(long projectId); @Query("SELECT t FROM Task t JOIN FETCH t.project LEFT JOIN FETCH t.sprint LEFT JOIN FETCH t.parent WHERE t.id =:id") diff --git a/src/main/java/com/javarush/jira/bugtracking/task/TaskService.java b/src/main/java/com/javarush/jira/bugtracking/task/TaskService.java index e6f385548..b26305956 100644 --- a/src/main/java/com/javarush/jira/bugtracking/task/TaskService.java +++ b/src/main/java/com/javarush/jira/bugtracking/task/TaskService.java @@ -21,6 +21,8 @@ import java.time.LocalDateTime; import java.util.List; +import java.util.Objects; +import java.util.Set; import static com.javarush.jira.bugtracking.ObjectType.TASK; import static com.javarush.jira.bugtracking.task.TaskUtil.fillExtraFields; @@ -71,6 +73,12 @@ public void changeSprint(long taskId, Long sprintId) { handler.getRepository().setTaskAndSubTasksSprint(taskId, sprintId); } + @Transactional + public void changeTags(long taskId, Set tags) { + Task task = handler.getRepository().getExisted(taskId); + task.getTags().addAll(Objects.requireNonNullElse(tags, Set.of())); + } + @Transactional public Task create(TaskToExt taskTo) { Task created = handler.createWithBelong(taskTo, TASK, "task_author"); @@ -86,6 +94,7 @@ public void update(TaskToExt taskTo, long id) { } } + @Transactional public TaskToFull get(long id) { Task task = Util.checkExist(id, handler.getRepository().findFullById(id)); TaskToFull taskToFull = fullMapper.toTo(task); diff --git a/src/main/java/com/javarush/jira/bugtracking/task/TaskUIController.java b/src/main/java/com/javarush/jira/bugtracking/task/TaskUIController.java index fbcc46aa2..2a793d9ca 100644 --- a/src/main/java/com/javarush/jira/bugtracking/task/TaskUIController.java +++ b/src/main/java/com/javarush/jira/bugtracking/task/TaskUIController.java @@ -111,6 +111,7 @@ private void addTaskInfo(Model model, TaskToFull taskTo) { model.addAttribute("comments", comments); model.addAttribute("attachs", attachmentHandler.getRepository().getAllForObject(taskTo.id(), ObjectType.TASK)); model.addAttribute("activities", taskTo.getActivityTos()); + model.addAttribute("tags", taskTo.getTags()); } private void addRefs(Model model, String currentStatus) { diff --git a/src/main/java/com/javarush/jira/bugtracking/task/to/TaskTo.java b/src/main/java/com/javarush/jira/bugtracking/task/to/TaskTo.java index a284403d8..2f85d6a33 100644 --- a/src/main/java/com/javarush/jira/bugtracking/task/to/TaskTo.java +++ b/src/main/java/com/javarush/jira/bugtracking/task/to/TaskTo.java @@ -8,6 +8,13 @@ import lombok.Getter; import lombok.Setter; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import static java.util.Objects.*; + @Getter public class TaskTo extends TitleTo implements HasCode, HasIdAndParentId { @Code @@ -19,13 +26,16 @@ public class TaskTo extends TitleTo implements HasCode, HasIdAndParentId { @Setter @Code private String statusCode; + private final Set tags = new HashSet<>(); - public TaskTo(Long id, String code, String title, String typeCode, String statusCode, Long parentId, Long projectId, Long sprintId) { + public TaskTo(Long id, String code, String title, String typeCode, String statusCode, + Long parentId, Long projectId, Long sprintId, Set tags) { super(id, code, title); this.typeCode = typeCode; this.statusCode = statusCode; this.parentId = parentId; this.projectId = projectId; this.sprintId = sprintId; + this.tags.addAll(requireNonNullElse(tags, Set.of())); } } diff --git a/src/main/java/com/javarush/jira/bugtracking/task/to/TaskToExt.java b/src/main/java/com/javarush/jira/bugtracking/task/to/TaskToExt.java index e49e22dd2..45b8b7863 100644 --- a/src/main/java/com/javarush/jira/bugtracking/task/to/TaskToExt.java +++ b/src/main/java/com/javarush/jira/bugtracking/task/to/TaskToExt.java @@ -10,6 +10,7 @@ import java.time.LocalDateTime; import java.util.Objects; +import java.util.Set; @Getter @Setter @@ -28,9 +29,10 @@ public class TaskToExt extends TaskTo { @Positive Integer estimate; - public TaskToExt(Long id, String code, String title, String description, String typeCode, String statusCode, String priorityCode, - LocalDateTime updated, Integer estimate, Long parentId, long projectId, Long sprintId) { - super(id, code, title, typeCode, statusCode, parentId, projectId, sprintId); + public TaskToExt(Long id, String code, String title, String description, String typeCode, + String statusCode, String priorityCode, LocalDateTime updated, + Integer estimate, Long parentId, long projectId, Long sprintId, Set tags) { + super(id, code, title, typeCode, statusCode, parentId, projectId, sprintId, tags); this.description = description; this.priorityCode = priorityCode; this.updated = updated; @@ -50,6 +52,7 @@ public boolean equals(Object o) { Objects.equals(estimate, taskToExt.estimate) && Objects.equals(parentId, taskToExt.parentId) && Objects.equals(projectId, taskToExt.projectId) && - Objects.equals(sprintId, taskToExt.sprintId); + Objects.equals(sprintId, taskToExt.sprintId) && + Objects.equals(getTags(), taskToExt.getTags()); } } diff --git a/src/main/java/com/javarush/jira/bugtracking/task/to/TaskToFull.java b/src/main/java/com/javarush/jira/bugtracking/task/to/TaskToFull.java index 090d48ef0..f69542204 100644 --- a/src/main/java/com/javarush/jira/bugtracking/task/to/TaskToFull.java +++ b/src/main/java/com/javarush/jira/bugtracking/task/to/TaskToFull.java @@ -6,6 +6,7 @@ import java.time.LocalDateTime; import java.util.List; +import java.util.Set; @Getter public class TaskToFull extends TaskToExt { @@ -15,10 +16,11 @@ public class TaskToFull extends TaskToExt { @Setter List activityTos; - public TaskToFull(Long id, String code, String title, String description, String typeCode, String statusCode, String priorityCode, - LocalDateTime updated, Integer estimate, CodeTo parent, CodeTo project, CodeTo sprint, List activityTos) { + public TaskToFull(Long id, String code, String title, String description, String typeCode, String statusCode, + String priorityCode, LocalDateTime updated, Integer estimate, CodeTo parent, CodeTo project, + CodeTo sprint, List activityTos, Set tags) { super(id, code, title, description, typeCode, statusCode, priorityCode, updated, estimate, - parent == null ? null : parent.getId(), project.getId(), sprint == null ? null : sprint.getId()); + parent == null ? null : parent.getId(), project.getId(), sprint == null ? null : sprint.getId(), tags); this.parent = parent; this.project = project; this.sprint = sprint; diff --git a/src/test/java/com/javarush/jira/bugtracking/task/TaskControllerTest.java b/src/test/java/com/javarush/jira/bugtracking/task/TaskControllerTest.java index b5c25e992..a63a7e6f2 100644 --- a/src/test/java/com/javarush/jira/bugtracking/task/TaskControllerTest.java +++ b/src/test/java/com/javarush/jira/bugtracking/task/TaskControllerTest.java @@ -12,6 +12,8 @@ import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import java.util.Set; + import static com.javarush.jira.bugtracking.ObjectType.TASK; import static com.javarush.jira.bugtracking.task.TaskController.REST_URL; import static com.javarush.jira.bugtracking.task.TaskService.CANNOT_ASSIGN; @@ -32,6 +34,7 @@ class TaskControllerTest extends AbstractControllerTest { private static final String ACTIVITIES_REST_URL = REST_URL + "/activities"; private static final String ACTIVITIES_REST_URL_SLASH = REST_URL + "/activities/"; private static final String CHANGE_STATUS = "/change-status"; + private static final String CHANGE_TAGS = "/change-tags"; private static final String PROJECT_ID = "projectId"; private static final String SPRINT_ID = "sprintId"; @@ -122,7 +125,7 @@ void updateTask() throws Exception { void updateTaskWhenStateNotChanged() throws Exception { int activitiesCount = activityRepository.findAllByTaskIdOrderByUpdatedDesc(TASK2_ID).size(); TaskToExt sameStateTo = new TaskToExt(TASK2_ID, taskTo2.getCode(), taskTo2.getTitle(), "Trees desc", taskTo2.getTypeCode(), - taskTo2.getStatusCode(), "normal", null, 4, taskTo2.getParentId(), taskTo2.getProjectId(), taskTo2.getSprintId()); + taskTo2.getStatusCode(), "normal", null, 4, taskTo2.getParentId(), taskTo2.getProjectId(), taskTo2.getSprintId(), taskTo2.getTags()); perform(MockMvcRequestBuilders.put(TASKS_REST_URL_SLASH + TASK2_ID) .contentType(MediaType.APPLICATION_JSON) .content(writeValue(sameStateTo))) @@ -142,7 +145,7 @@ void updateTaskUnauthorized() throws Exception { @Test @WithUserDetails(value = ADMIN_MAIL) void updateTaskWhenProjectNotExists() throws Exception { - TaskToExt notExistsProjectTo = new TaskToExt(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "in_progress", "high", null, 4, null, NOT_FOUND, SPRINT1_ID); + TaskToExt notExistsProjectTo = new TaskToExt(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "in_progress", "high", null, 4, null, NOT_FOUND, SPRINT1_ID, Set.of()); perform(MockMvcRequestBuilders.put(TASKS_REST_URL_SLASH + TASK2_ID) .contentType(MediaType.APPLICATION_JSON) .content(writeValue(notExistsProjectTo))) @@ -163,7 +166,7 @@ void updateTaskIdNotConsistent() throws Exception { @Test @WithUserDetails(value = ADMIN_MAIL) void updateTaskWhenChangeProject() throws Exception { - TaskToExt changedProjectTo = new TaskToExt(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "in_progress", "high", null, 4, null, PROJECT1_ID + 1, SPRINT1_ID); + TaskToExt changedProjectTo = new TaskToExt(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "in_progress", "high", null, 4, null, PROJECT1_ID + 1, SPRINT1_ID, Set.of()); perform(MockMvcRequestBuilders.put(TASKS_REST_URL_SLASH + TASK2_ID) .contentType(MediaType.APPLICATION_JSON) .content(writeValue(changedProjectTo))) @@ -174,7 +177,7 @@ void updateTaskWhenChangeProject() throws Exception { @Test @WithUserDetails(value = USER_MAIL) void updateSprintIdWhenDev() throws Exception { - TaskToExt changedSprintTo = new TaskToExt(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "in_progress", "high", null, 4, null, PROJECT1_ID, SPRINT1_ID + 1); + TaskToExt changedSprintTo = new TaskToExt(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "in_progress", "high", null, 4, null, PROJECT1_ID, SPRINT1_ID + 1, Set.of()); perform(MockMvcRequestBuilders.put(TASKS_REST_URL_SLASH + TASK2_ID) .contentType(MediaType.APPLICATION_JSON) .content(writeValue(changedSprintTo))) @@ -185,7 +188,7 @@ void updateSprintIdWhenDev() throws Exception { @Test @WithUserDetails(value = ADMIN_MAIL) void updateSprintIdWhenAdmin() throws Exception { - TaskToExt changedSprintTo = new TaskToExt(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "in_progress", "high", null, 4, null, PROJECT1_ID, SPRINT1_ID + 1); + TaskToExt changedSprintTo = new TaskToExt(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "in_progress", "high", null, 4, null, PROJECT1_ID, SPRINT1_ID + 1, Set.of()); perform(MockMvcRequestBuilders.put(TASKS_REST_URL_SLASH + TASK2_ID) .contentType(MediaType.APPLICATION_JSON) .content(writeValue(changedSprintTo))) @@ -197,7 +200,7 @@ void updateSprintIdWhenAdmin() throws Exception { @Test @WithUserDetails(value = MANAGER_MAIL) void updateSprintIdWhenManager() throws Exception { - TaskToExt changedSprintTo = new TaskToExt(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "in_progress", "high", null, 4, null, PROJECT1_ID, SPRINT1_ID + 1); + TaskToExt changedSprintTo = new TaskToExt(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "in_progress", "high", null, 4, null, PROJECT1_ID, SPRINT1_ID + 1, Set.of()); perform(MockMvcRequestBuilders.put(TASKS_REST_URL_SLASH + TASK2_ID) .contentType(MediaType.APPLICATION_JSON) .content(writeValue(changedSprintTo))) @@ -376,7 +379,7 @@ void createTaskUnauthorized() throws Exception { @Test @WithUserDetails(value = ADMIN_MAIL) void createTaskInvalid() throws Exception { - TaskToExt invalidTo = new TaskToExt(null, "", null, null, "epic", null, null, null, 3, null, PROJECT1_ID, SPRINT1_ID); + TaskToExt invalidTo = new TaskToExt(null, "", null, null, "epic", null, null, null, 3, null, PROJECT1_ID, SPRINT1_ID, Set.of()); perform(MockMvcRequestBuilders.post(REST_URL) .contentType(MediaType.APPLICATION_JSON) .content(writeValue(invalidTo))) @@ -387,7 +390,7 @@ void createTaskInvalid() throws Exception { @Test @WithUserDetails(value = ADMIN_MAIL) void createTaskWhenProjectNotExists() throws Exception { - TaskToExt notExistsProjectTo = new TaskToExt(null, "epic-1", "Data New", "task NEW", "epic", "in_progress", "low", null, 3, null, NOT_FOUND, SPRINT1_ID); + TaskToExt notExistsProjectTo = new TaskToExt(null, "epic-1", "Data New", "task NEW", "epic", "in_progress", "low", null, 3, null, NOT_FOUND, SPRINT1_ID, Set.of()); perform(MockMvcRequestBuilders.post(REST_URL) .contentType(MediaType.APPLICATION_JSON) .content(writeValue(notExistsProjectTo))) @@ -590,4 +593,15 @@ void unAssignFromTaskWhenNotAssigned() throws Exception { .andExpect(jsonPath("$.detail", is(String .format("Not found assignment with userType=%s for task {%d} for user {%d}", TASK_DEVELOPER, TASK1_ID, ADMIN_ID)))); } + + @Test + @WithUserDetails(value = ADMIN_MAIL) + void changeTaskTags() throws Exception { + perform(MockMvcRequestBuilders.patch(TASKS_REST_URL_SLASH + TASK1_ID + CHANGE_TAGS) + .contentType(MediaType.APPLICATION_JSON) + .content(writeValue(updateTagsForTask1))) + .andExpect(status().isNoContent()) + .andDo(print()); + } + } diff --git a/src/test/java/com/javarush/jira/bugtracking/task/TaskTestData.java b/src/test/java/com/javarush/jira/bugtracking/task/TaskTestData.java index 060449017..ed53eaaa2 100644 --- a/src/test/java/com/javarush/jira/bugtracking/task/TaskTestData.java +++ b/src/test/java/com/javarush/jira/bugtracking/task/TaskTestData.java @@ -9,6 +9,7 @@ import com.javarush.jira.common.to.CodeTo; import java.util.List; +import java.util.Set; import static com.javarush.jira.bugtracking.ObjectType.TASK; import static com.javarush.jira.login.internal.web.UserTestData.ADMIN_ID; @@ -42,10 +43,10 @@ public class TaskTestData { public static final String TASK_DEVELOPER = "task_developer"; public static final String TASK_REVIEWER = "task_reviewer"; - public static final TaskTo taskTo1 = new TaskTo(TASK1_ID, "epic-" + TASK1_ID, "Data", "epic", "in_progress", null, PROJECT1_ID, SPRINT1_ID); - public static final TaskTo taskTo2 = new TaskTo(TASK2_ID, "epic-" + TASK2_ID, "Trees", "epic", "in_progress", null, PROJECT1_ID, SPRINT1_ID); - public static final TaskToFull taskToFull1 = new TaskToFull(TASK1_ID, "epic-1", "Data", null, "epic", "in_progress", "normal", null, 4, null, new CodeTo(PROJECT1_ID, "PR1"), new CodeTo(SPRINT1_ID, "SP-1.001"), null); - public static final TaskToFull taskToFull2 = new TaskToFull(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "ready_for_review", "high", null, 4, null, new CodeTo(PROJECT1_ID, "PR1"), new CodeTo(SPRINT1_ID, "SP-1.001"), null); + public static final TaskTo taskTo1 = new TaskTo(TASK1_ID, "epic-" + TASK1_ID, "Data", "epic", "in_progress", null, PROJECT1_ID, SPRINT1_ID, Set.of()); + public static final TaskTo taskTo2 = new TaskTo(TASK2_ID, "epic-" + TASK2_ID, "Trees", "epic", "in_progress", null, PROJECT1_ID, SPRINT1_ID, Set.of()); + public static final TaskToFull taskToFull1 = new TaskToFull(TASK1_ID, "epic-1", "Data", null, "epic", "in_progress", "normal", null, 4, null, new CodeTo(PROJECT1_ID, "PR1"), new CodeTo(SPRINT1_ID, "SP-1.001"), null, Set.of()); + public static final TaskToFull taskToFull2 = new TaskToFull(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "ready_for_review", "high", null, 4, null, new CodeTo(PROJECT1_ID, "PR1"), new CodeTo(SPRINT1_ID, "SP-1.001"), null, Set.of()); public static final ActivityTo activityTo1ForTask1 = new ActivityTo(ACTIVITY1_ID, TASK1_ID, USER_ID, null, null, "in_progress", "low", "epic", "Data", null, 3, null); public static final ActivityTo activityTo2ForTask1 = new ActivityTo(ACTIVITY1_ID + 1, TASK1_ID, ADMIN_ID, null, null, null, "normal", null, "Data", null, null, null); public static final ActivityTo activityTo3ForTask1 = new ActivityTo(ACTIVITY1_ID + 2, TASK1_ID, USER_ID, null, null, null, null, null, "Data", null, 4, null); @@ -53,6 +54,7 @@ public class TaskTestData { public static final ActivityTo activityTo1ForTask2 = new ActivityTo(ACTIVITY1_ID + 3, TASK2_ID, USER_ID, null, null, "in_progress", "normal", "epic", "Trees", "Trees desc", 4, null); public static final ActivityTo updatePriorityCode = new ActivityTo(ACTIVITY1_ID + 4, TASK2_ID, USER_ID, null, null, "ready_for_review", "high", "epic", "Trees UPD", "task UPD", 4, null); public static final List activityTosForTask2 = List.of(updatePriorityCode, activityTo1ForTask2); + public static final TaskTo updateTagsForTask1 = new TaskTo(TASK1_ID, null, null, null, null, null, null, null, Set.of("tag1", "tag2")); public static final UserBelong userTask1Assignment1 = new UserBelong(1L, TASK, USER_ID, "task_developer"); public static final UserBelong userTask1Assignment2 = new UserBelong(1L, TASK, USER_ID, "task_tester"); @@ -65,7 +67,7 @@ public class TaskTestData { } public static TaskToExt getNewTaskTo() { - return new TaskToExt(null, "epic-1", "Data New", "task NEW", "epic", "in_progress", "low", null, 3, null, PROJECT1_ID, SPRINT1_ID); + return new TaskToExt(null, "epic-1", "Data New", "task NEW", "epic", "in_progress", "low", null, 3, null, PROJECT1_ID, SPRINT1_ID, Set.of()); } public static ActivityTo getNewActivityTo() { @@ -73,10 +75,11 @@ public static ActivityTo getNewActivityTo() { } public static TaskToExt getUpdatedTaskTo() { - return new TaskToExt(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "ready_for_review", "high", null, 4, null, PROJECT1_ID, SPRINT1_ID); + return new TaskToExt(TASK2_ID, "epic-2", "Trees UPD", "task UPD", "epic", "ready_for_review", "high", null, 4, null, PROJECT1_ID, SPRINT1_ID, Set.of()); } public static ActivityTo getUpdatedActivityTo() { return new ActivityTo(ACTIVITY1_ID, TASK1_ID, USER_ID, null, null, "in_progress", "low", "epic", null, null, 3, null); } + } From 90981e2c8a4ec0ae5464051a5fe05aa71fd6f181 Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Wed, 4 Sep 2024 16:44:33 +0400 Subject: [PATCH 07/15] Completed tasks 9,10. ADDED Dockerfile, docker-compose.yml --- .env | 16 ++++++++++++++ Dockerfile | 5 +++++ README.md | 10 ++++++++- config/nginx.conf | 4 ++-- docker-compose.yml | 34 +++++++++++++++++++++++++++++ src/main/resources/application.yaml | 4 ++-- 6 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 .env create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.env b/.env new file mode 100644 index 000000000..e9839b236 --- /dev/null +++ b/.env @@ -0,0 +1,16 @@ +# .env + +APP_HOST_URL=http://jira_rush:8080 +SPRING_DATASOURCE_URL=jdbc:postgresql://jira_db:5432/jira +DATASOURCE_PASSWORD=JiraRush +DATASOURCE_USERNAME=jira +OAUTH2_GITHUB_CLIENT_ID=3d0d8738e65881fff266 +OAUTH2_GITHUB_CLIENT_SECRET=0f97031ce6178b7dfb67a6af587f37e222a16120 +OAUTH2_GOOGLE_CLIENT_ID=329113642700-f8if6pu68j2repq3ef6umd5jgiliup60.apps.googleusercontent.com +OAUTH2_GOOGLE_CLIENT_SECRET=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 +OAUTH2_GITLAB_CLIENT_ID=b8520a3266089063c0d8261cce36971defa513f5ffd9f9b7a3d16728fc83a494 +OAUTH2_GITLAB_CLIENT_SECRET=e72c65320cf9d6495984a37b0f9cc03ec46be0bb6f071feaebbfe75168117004 +MAIL_HOST=smtp.gmail.com +MAIL_USERNAME=jira4jr@gmail.com +MAIL_PASSWORD=zdfzsrqvgimldzyj +MAIL_PORT=587 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..cda0af60f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:17-oracle +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +COPY ./resources /resources +ENTRYPOINT ["java","-jar","/app.jar"] \ No newline at end of file diff --git a/README.md b/README.md index 719b268f5..0c015447b 100644 --- a/README.md +++ b/README.md @@ -27,4 +27,12 @@ - https://habr.com/ru/articles/259055/ Список выполненных задач: -... \ No newline at end of file +1-7, 9,10 + +Для запуска задач 9, 10 выполнить следующие команды, используя jdk 17 + +``` +mvn clean install -P prod +docker compose up +``` +После перейти по адресу http://localhost:8080 \ No newline at end of file diff --git a/config/nginx.conf b/config/nginx.conf index 82b9e234d..1af993591 100644 --- a/config/nginx.conf +++ b/config/nginx.conf @@ -27,11 +27,11 @@ server { location ~ (/$|/view/|/ui/|/oauth2/) { expires 0m; - proxy_pass http://localhost:8080; + proxy_pass http://jira_rush:8080; proxy_connect_timeout 30s; } location ~ (/api/|/doc|/swagger-ui/|/v3/api-docs/) { - proxy_pass http://localhost:8080; + proxy_pass http://jira_rush:8080; proxy_connect_timeout 150s; } location / { diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..c46d16fb6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,34 @@ +version: '3.9' +services: + jira_rush: + container_name: jira_rush + build: + context: . + dockerfile: Dockerfile + env_file: + - .env + ports: + - "8080:8080" + depends_on: + - jira_db + + jira_db: + container_name: jira_db + image: postgres:16.2 + ports: + - "5432:5432" + environment: + POSTGRES_DB: jira + POSTGRES_USER: "${DATASOURCE_USERNAME}" + POSTGRES_PASSWORD: "${DATASOURCE_PASSWORD}" + + nginx: + container_name: nginx + image: nginx:latest + restart: always + ports: + - "80:80" + volumes: + - ./config/nginx.conf:/etc/nginx/conf.d/nginx.conf + depends_on: + - jira_rush \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 6391da91b..5bfb9e5c0 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,6 +1,6 @@ # https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html app: - host-url: http://localhost:8080 + host-url: ${APP_HOST_URL} test-mail: jira4jr@gmail.com templates-update-cache: 5s mail-sending-props: @@ -31,7 +31,7 @@ spring: jdbc.batch_size: 20 auto_quote_keyword: true datasource: - url: jdbc:postgresql://localhost:5432/jira + url: ${SPRING_DATASOURCE_URL} liquibase: changeLog: "classpath:db/changelog.sql" From 2bc54e4a1bb8752c582246c8abcb2bd9d79ae221 Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Thu, 5 Sep 2024 16:44:30 +0400 Subject: [PATCH 08/15] Completed task 8. ADDED methods for calculate time spent on work and test --- README.md | 8 ++-- localhost.env | 16 ++++++++ .../bugtracking/task/ActivityService.java | 39 +++++++++++++++++++ src/main/resources/db/changelog.sql | 9 +++++ 4 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 localhost.env diff --git a/README.md b/README.md index 0c015447b..389fd868f 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,10 @@ - https://habr.com/ru/articles/259055/ -Список выполненных задач: -1-7, 9,10 - -Для запуска задач 9, 10 выполнить следующие команды, используя jdk 17 +**Список выполненных задач:** +1-10 \ +Для запуска без docker использовать файл localhost.env \ +Для запуска задач 9, 10 выполнить следующие команды, **используя jdk 17** ``` mvn clean install -P prod diff --git a/localhost.env b/localhost.env new file mode 100644 index 000000000..9f530819b --- /dev/null +++ b/localhost.env @@ -0,0 +1,16 @@ +# .env + +APP_HOST_URL=http://localhost:8080 +SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/jira +DATASOURCE_PASSWORD=JiraRush +DATASOURCE_USERNAME=jira +OAUTH2_GITHUB_CLIENT_ID=3d0d8738e65881fff266 +OAUTH2_GITHUB_CLIENT_SECRET=0f97031ce6178b7dfb67a6af587f37e222a16120 +OAUTH2_GOOGLE_CLIENT_ID=329113642700-f8if6pu68j2repq3ef6umd5jgiliup60.apps.googleusercontent.com +OAUTH2_GOOGLE_CLIENT_SECRET=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 +OAUTH2_GITLAB_CLIENT_ID=b8520a3266089063c0d8261cce36971defa513f5ffd9f9b7a3d16728fc83a494 +OAUTH2_GITLAB_CLIENT_SECRET=e72c65320cf9d6495984a37b0f9cc03ec46be0bb6f071feaebbfe75168117004 +MAIL_HOST=smtp.gmail.com +MAIL_USERNAME=jira4jr@gmail.com +MAIL_PASSWORD=zdfzsrqvgimldzyj +MAIL_PORT=587 \ No newline at end of file diff --git a/src/main/java/com/javarush/jira/bugtracking/task/ActivityService.java b/src/main/java/com/javarush/jira/bugtracking/task/ActivityService.java index 7938541bb..b2b9b0231 100644 --- a/src/main/java/com/javarush/jira/bugtracking/task/ActivityService.java +++ b/src/main/java/com/javarush/jira/bugtracking/task/ActivityService.java @@ -3,11 +3,14 @@ import com.javarush.jira.bugtracking.Handlers; import com.javarush.jira.bugtracking.task.to.ActivityTo; import com.javarush.jira.common.error.DataConflictException; +import com.javarush.jira.common.error.NotFoundException; import com.javarush.jira.login.AuthUser; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.Duration; +import java.time.LocalDateTime; import java.util.List; import static com.javarush.jira.bugtracking.task.TaskUtil.getLatestValue; @@ -15,9 +18,14 @@ @Service @RequiredArgsConstructor public class ActivityService { + public static final String STATUS_READY_FOR_REVIEW = "ready_for_review"; + public static final String STATUS_IN_PROGRESS = "in_progress"; + public static final String STATUS_DONE = "done"; + public static final String TIME_WITH_DAYS_FORMAT = "%d d %d h %d m %d s"; private final TaskRepository taskRepository; private final Handlers.ActivityHandler handler; + private final ActivityRepository activityRepository; private static void checkBelong(HasAuthorId activity) { if (activity.getAuthorId() != AuthUser.authId()) { @@ -73,4 +81,35 @@ private void updateTaskIfRequired(long taskId, String activityStatus, String act } } } + + public String getTimeSpentOnWork(long taskId) { + return getTimeBetweenStatus(taskId, STATUS_IN_PROGRESS, STATUS_READY_FOR_REVIEW); + } + + public String getTimeSpentOnTest(long taskId) { + return getTimeBetweenStatus(taskId, STATUS_READY_FOR_REVIEW, STATUS_DONE); + } + + private String getTimeBetweenStatus(long taskId, String startStatus, String endStatus) { + taskRepository.getExisted(taskId); + LocalDateTime start = getActivityUpdatedByTaskIdAndStatus(taskId, startStatus); + LocalDateTime end = getActivityUpdatedByTaskIdAndStatus(taskId, endStatus); + Duration duration = Duration.between(start, end); + return TIME_WITH_DAYS_FORMAT.formatted(duration.toDaysPart(), + duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart() + ); + } + + private LocalDateTime getActivityUpdatedByTaskIdAndStatus(long taskId, String status) { + return activityRepository + .findAllByTaskIdOrderByUpdatedDesc(taskId) + .stream() + .filter(activity -> activity.getUpdated() != null) + .filter(activity -> status.equals(activity.getStatusCode())) + .findFirst() + .orElseThrow( + () -> new NotFoundException("Task " + taskId + " doesn't have activity with status "+ status) + ) + .getUpdated(); + } } diff --git a/src/main/resources/db/changelog.sql b/src/main/resources/db/changelog.sql index 1595f97d3..de756f386 100644 --- a/src/main/resources/db/changelog.sql +++ b/src/main/resources/db/changelog.sql @@ -328,3 +328,12 @@ values ('todo', 'ToDo', 3, 'in_progress,canceled|'), drop index UK_USER_BELONG; create unique index UK_USER_BELONG on USER_BELONG (OBJECT_ID, OBJECT_TYPE, USER_ID, USER_TYPE_CODE) where ENDPOINT is null; + +--changeset khasanov:insert_values_into_ACTIVITY + +insert into ACTIVITY (AUTHOR_ID, TASK_ID, UPDATED, STATUS_CODE ) +values (2, 73, '2024-09-04 18:59:43.018480', 'in_progress'), + (2, 73, '2024-09-04 20:22:10.680585', 'ready_for_review'), + (2, 73, '2024-09-05 15:22:10.680585', 'done') + + From ada85dce6fa29182ad7f00831a1b86b269f0b35d Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Fri, 6 Sep 2024 15:34:04 +0400 Subject: [PATCH 09/15] Completed task 8. MOVED insert script into data.sql --- README.md | 1 + src/main/resources/data4dev/data.sql | 9 +++++++++ src/main/resources/db/changelog.sql | 9 --------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 389fd868f..f5d1e5870 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ **Список выполненных задач:** 1-10 \ Для запуска без docker использовать файл localhost.env \ +После запуска нужно выполнить скрипт resources/data4dev/data.sql \ Для запуска задач 9, 10 выполнить следующие команды, **используя jdk 17** ``` diff --git a/src/main/resources/data4dev/data.sql b/src/main/resources/data4dev/data.sql index 28b494155..6bfa95e22 100644 --- a/src/main/resources/data4dev/data.sql +++ b/src/main/resources/data4dev/data.sql @@ -324,3 +324,12 @@ values (6, 1, '2023-05-15 09:05:10', null, 'Data', null, 3, 'epic', 'in_progress (5, 118, '2023-05-16 11:10:10', null, 'UI tab of tasks', null, null, null, null, 'high'), (11, 118, '2023-05-16 12:30:10', null, 'UI tab of tasks', null, 2, null, null, null); +---task 8---- +insert into ACTIVITY (AUTHOR_ID, TASK_ID, UPDATED, STATUS_CODE ) +values (2, 73, '2024-09-04 18:59:43.018480', 'in_progress'), + (2, 73, '2024-09-04 20:22:10.680585', 'ready_for_review'), + (2, 73, '2024-09-05 15:22:10.680585', 'done') + +update TASK +set status_code = 'done' +where id = 73 \ No newline at end of file diff --git a/src/main/resources/db/changelog.sql b/src/main/resources/db/changelog.sql index de756f386..1595f97d3 100644 --- a/src/main/resources/db/changelog.sql +++ b/src/main/resources/db/changelog.sql @@ -328,12 +328,3 @@ values ('todo', 'ToDo', 3, 'in_progress,canceled|'), drop index UK_USER_BELONG; create unique index UK_USER_BELONG on USER_BELONG (OBJECT_ID, OBJECT_TYPE, USER_ID, USER_TYPE_CODE) where ENDPOINT is null; - ---changeset khasanov:insert_values_into_ACTIVITY - -insert into ACTIVITY (AUTHOR_ID, TASK_ID, UPDATED, STATUS_CODE ) -values (2, 73, '2024-09-04 18:59:43.018480', 'in_progress'), - (2, 73, '2024-09-04 20:22:10.680585', 'ready_for_review'), - (2, 73, '2024-09-05 15:22:10.680585', 'done') - - From 51a78d8ca6dd4983f4935e2bace1af7466b2dc12 Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Fri, 6 Sep 2024 15:58:01 +0400 Subject: [PATCH 10/15] fix localhost.env --- localhost.env | 1 - 1 file changed, 1 deletion(-) diff --git a/localhost.env b/localhost.env index 9f530819b..888168025 100644 --- a/localhost.env +++ b/localhost.env @@ -1,7 +1,6 @@ # .env APP_HOST_URL=http://localhost:8080 -SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/jira DATASOURCE_PASSWORD=JiraRush DATASOURCE_USERNAME=jira OAUTH2_GITHUB_CLIENT_ID=3d0d8738e65881fff266 From 48328c520a4f94f67c9313a9360f574e9abd3d09 Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Fri, 6 Sep 2024 16:08:44 +0400 Subject: [PATCH 11/15] CHANGED README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f5d1e5870..3fcbeb90f 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,10 @@ **Список выполненных задач:** 1-10 \ -Для запуска без docker использовать файл localhost.env \ +Для запуска без docker использовать файл localhost.env или вставить переменные окружения +``` +APP_HOST_URL=http://localhost:8080; DATASOURCE_PASSWORD=JiraRush; DATASOURCE_USERNAME=jira; OAUTH2_GITHUB_CLIENT_ID=3d0d8738e65881fff266; OAUTH2_GITHUB_CLIENT_SECRET=0f97031ce6178b7dfb67a6af587f37e222a16120; OAUTH2_GOOGLE_CLIENT_ID=329113642700-f8if6pu68j2repq3ef6umd5jgiliup60.apps.googleusercontent.com; OAUTH2_GOOGLE_CLIENT_SECRET=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; OAUTH2_GITLAB_CLIENT_ID=b8520a3266089063c0d8261cce36971defa513f5ffd9f9b7a3d16728fc83a494; OAUTH2_GITLAB_CLIENT_SECRET=e72c65320cf9d6495984a37b0f9cc03ec46be0bb6f071feaebbfe75168117004; MAIL_HOST=smtp.gmail.com; MAIL_USERNAME=jira4jr@gmail.com; MAIL_PASSWORD=zdfzsrqvgimldzyj; MAIL_PORT=587 +``` После запуска нужно выполнить скрипт resources/data4dev/data.sql \ Для запуска задач 9, 10 выполнить следующие команды, **используя jdk 17** From 232e330cb384bc07b6d603d212dd46559e3417db Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Fri, 6 Sep 2024 16:27:48 +0400 Subject: [PATCH 12/15] fix application.yaml, localhost.env, README.md --- README.md | 2 +- localhost.env | 1 + src/main/resources/application.yaml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3fcbeb90f..d9a9a7128 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ 1-10 \ Для запуска без docker использовать файл localhost.env или вставить переменные окружения ``` -APP_HOST_URL=http://localhost:8080; DATASOURCE_PASSWORD=JiraRush; DATASOURCE_USERNAME=jira; OAUTH2_GITHUB_CLIENT_ID=3d0d8738e65881fff266; OAUTH2_GITHUB_CLIENT_SECRET=0f97031ce6178b7dfb67a6af587f37e222a16120; OAUTH2_GOOGLE_CLIENT_ID=329113642700-f8if6pu68j2repq3ef6umd5jgiliup60.apps.googleusercontent.com; OAUTH2_GOOGLE_CLIENT_SECRET=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; OAUTH2_GITLAB_CLIENT_ID=b8520a3266089063c0d8261cce36971defa513f5ffd9f9b7a3d16728fc83a494; OAUTH2_GITLAB_CLIENT_SECRET=e72c65320cf9d6495984a37b0f9cc03ec46be0bb6f071feaebbfe75168117004; MAIL_HOST=smtp.gmail.com; MAIL_USERNAME=jira4jr@gmail.com; MAIL_PASSWORD=zdfzsrqvgimldzyj; MAIL_PORT=587 +APP_HOST_URL=http://localhost:8080; DATASOURCE_URL=jdbc:postgresql://localhost:5432/jira; DATASOURCE_PASSWORD=JiraRush; DATASOURCE_USERNAME=jira; OAUTH2_GITHUB_CLIENT_ID=3d0d8738e65881fff266; OAUTH2_GITHUB_CLIENT_SECRET=0f97031ce6178b7dfb67a6af587f37e222a16120; OAUTH2_GOOGLE_CLIENT_ID=329113642700-f8if6pu68j2repq3ef6umd5jgiliup60.apps.googleusercontent.com; OAUTH2_GOOGLE_CLIENT_SECRET=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; OAUTH2_GITLAB_CLIENT_ID=b8520a3266089063c0d8261cce36971defa513f5ffd9f9b7a3d16728fc83a494; OAUTH2_GITLAB_CLIENT_SECRET=e72c65320cf9d6495984a37b0f9cc03ec46be0bb6f071feaebbfe75168117004; MAIL_HOST=smtp.gmail.com; MAIL_USERNAME=jira4jr@gmail.com; MAIL_PASSWORD=zdfzsrqvgimldzyj; MAIL_PORT=587 ``` После запуска нужно выполнить скрипт resources/data4dev/data.sql \ Для запуска задач 9, 10 выполнить следующие команды, **используя jdk 17** diff --git a/localhost.env b/localhost.env index 888168025..77d8bdeae 100644 --- a/localhost.env +++ b/localhost.env @@ -1,6 +1,7 @@ # .env APP_HOST_URL=http://localhost:8080 +DATASOURCE_URL=jdbc:postgresql://localhost:5432/jira DATASOURCE_PASSWORD=JiraRush DATASOURCE_USERNAME=jira OAUTH2_GITHUB_CLIENT_ID=3d0d8738e65881fff266 diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 5bfb9e5c0..44fff13b7 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -31,7 +31,7 @@ spring: jdbc.batch_size: 20 auto_quote_keyword: true datasource: - url: ${SPRING_DATASOURCE_URL} + url: ${DATASOURCE_URL} liquibase: changeLog: "classpath:db/changelog.sql" From fcea9a8dbdd1c7a04b7ca78fa2013fe64ea0c710 Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Fri, 6 Sep 2024 16:28:33 +0400 Subject: [PATCH 13/15] fix data.sql --- src/main/resources/data4dev/data.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/data4dev/data.sql b/src/main/resources/data4dev/data.sql index 6bfa95e22..877e88daa 100644 --- a/src/main/resources/data4dev/data.sql +++ b/src/main/resources/data4dev/data.sql @@ -328,8 +328,8 @@ values (6, 1, '2023-05-15 09:05:10', null, 'Data', null, 3, 'epic', 'in_progress insert into ACTIVITY (AUTHOR_ID, TASK_ID, UPDATED, STATUS_CODE ) values (2, 73, '2024-09-04 18:59:43.018480', 'in_progress'), (2, 73, '2024-09-04 20:22:10.680585', 'ready_for_review'), - (2, 73, '2024-09-05 15:22:10.680585', 'done') + (2, 73, '2024-09-05 15:22:10.680585', 'done'); update TASK set status_code = 'done' -where id = 73 \ No newline at end of file +where id = 73; \ No newline at end of file From 2179f6ae5dd9c37ea7a6fce9c54aa477cc432a22 Mon Sep 17 00:00:00 2001 From: Said-Khasanov Date: Sun, 8 Sep 2024 17:10:26 +0400 Subject: [PATCH 14/15] Completed task 11. ADDED en, ru, de localizations --- resources/mails/email-confirmation.html | 8 +++---- resources/view/index.html | 8 +++---- resources/view/layout/header.html | 22 ++++++++++++++----- resources/view/layout/sidebar.html | 15 ++++++++----- .../common/internal/config/MvcConfig.java | 21 ++++++++++++++++++ src/main/resources/messages.properties | 16 ++++++++++++++ src/main/resources/messages_de.properties | 16 ++++++++++++++ src/main/resources/messages_en.properties | 16 ++++++++++++++ src/main/resources/messages_ru.properties | 16 ++++++++++++++ 9 files changed, 119 insertions(+), 19 deletions(-) create mode 100644 src/main/resources/messages.properties create mode 100644 src/main/resources/messages_de.properties create mode 100644 src/main/resources/messages_en.properties create mode 100644 src/main/resources/messages_ru.properties diff --git a/resources/mails/email-confirmation.html b/resources/mails/email-confirmation.html index 106e6129a..aa3a58a45 100644 --- a/resources/mails/email-confirmation.html +++ b/resources/mails/email-confirmation.html @@ -1,13 +1,13 @@ - JiraRush - подтверждение почты + JiraRush - подтверждение почты -

-

Чтобы завершить настройку учетной записи и начать пользоваться JiraRush, подтвердите, что вы правильно указали вашу +

+

Чтобы завершить настройку учетной записи и начать пользоваться JiraRush, подтвердите, что вы правильно указали вашу электронную почту.

-Подтвердить почту +Подтвердить почту \ No newline at end of file diff --git a/resources/view/index.html b/resources/view/index.html index e8656ef96..359f323a2 100644 --- a/resources/view/index.html +++ b/resources/view/index.html @@ -1,13 +1,13 @@ - + - + -

JiraRush Home page

+

JiraRush Home page

- +
diff --git a/resources/view/layout/header.html b/resources/view/layout/header.html index 451f9652d..52cfbbb15 100644 --- a/resources/view/layout/header.html +++ b/resources/view/layout/header.html @@ -1,5 +1,5 @@ - +
@@ -7,9 +7,15 @@