diff --git a/resources/mails/email-confirmation.html b/resources/mails/email-confirmation.html
index 106e6129a..687157cd9 100644
--- a/resources/mails/email-confirmation.html
+++ b/resources/mails/email-confirmation.html
@@ -1,13 +1,12 @@
-
+
- JiraRush - подтверждение почты
+
-
-Чтобы завершить настройку учетной записи и начать пользоваться JiraRush, подтвердите, что вы правильно указали вашу
- электронную почту.
-Подтвердить почту
+
+
+
\ No newline at end of file
diff --git a/resources/view/index.html b/resources/view/index.html
index e8656ef96..bc32596ff 100644
--- a/resources/view/index.html
+++ b/resources/view/index.html
@@ -1,13 +1,15 @@
-
-
-
-
+
+
- JiraRush Home page
+ JiraRush Home page
+
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..63c5c8bc3 100644
--- a/resources/view/unauth/register.html
+++ b/resources/view/unauth/register.html
@@ -77,14 +77,7 @@ Registration
type="button">
-
-
-
-
-
-
+
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..2b79ce414 100644
--- a/src/main/java/com/javarush/jira/bugtracking/attachment/FileUtil.java
+++ b/src/main/java/com/javarush/jira/bugtracking/attachment/FileUtil.java
@@ -25,14 +25,18 @@ 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());
+ try {
+ Path dir = Paths.get(directoryPath);
+ if (Files.notExists(dir)) {
+ Files.createDirectories(dir);
}
+
+ Path filePath = dir.resolve(fileName);
+
+ Files.write(filePath, multipartFile.getBytes());
+
+ } catch (IOException ex) {
+ throw new IllegalRequestDataException("Failed to upload file " + multipartFile.getOriginalFilename());
}
}
diff --git a/src/main/java/com/javarush/jira/common/internal/config/AppConfig.java b/src/main/java/com/javarush/jira/common/internal/config/AppConfig.java
index fbc6ef804..2a6b94de0 100644
--- a/src/main/java/com/javarush/jira/common/internal/config/AppConfig.java
+++ b/src/main/java/com/javarush/jira/common/internal/config/AppConfig.java
@@ -5,18 +5,22 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.hibernate5.jakarta.Hibernate5JakartaModule;
import com.javarush.jira.common.util.JsonUtil;
+import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.http.ProblemDetail;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import javax.sql.DataSource;
import java.util.Map;
import java.util.concurrent.Executor;
@@ -52,6 +56,16 @@ public boolean isTest() {
return env.acceptsProfiles(Profiles.of("test"));
}
+ @Profile("prod")
+ public DataSource prodDataSource() {
+ return DataSourceBuilder.create().build();
+ }
+
+ @Profile("test")
+ public DataSource testDataSource() {
+ return DataSourceBuilder.create().build();
+ }
+
@Autowired
void configureAndStoreObjectMapper(ObjectMapper objectMapper) {
objectMapper.registerModule(new Hibernate5JakartaModule());
diff --git a/src/main/java/com/javarush/jira/common/internal/config/MvcConfig.java b/src/main/java/com/javarush/jira/common/internal/config/MvcConfig.java
index 8a434a807..2803fc2ce 100644
--- a/src/main/java/com/javarush/jira/common/internal/config/MvcConfig.java
+++ b/src/main/java/com/javarush/jira/common/internal/config/MvcConfig.java
@@ -7,21 +7,26 @@
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.ui.ModelMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
import org.springframework.web.filter.ForwardedHeaderFilter;
import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter;
+import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
+import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import org.springframework.web.servlet.mvc.UrlFilenameViewController;
import java.time.Duration;
+import java.util.Locale;
import java.util.Properties;
//@EnableWebMvc : http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration
@@ -32,6 +37,29 @@
public class MvcConfig implements WebMvcConfigurer {
private final AppProperties appProperties;
+
+ @Bean
+ public LocaleResolver localeResolver() {
+ SessionLocaleResolver localeResolver = new SessionLocaleResolver();
+ localeResolver.setDefaultLocale(Locale.ENGLISH);
+ return localeResolver;
+ }
+
+ @Bean
+ public ReloadableResourceBundleMessageSource messageSource() {
+ ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
+ messageSource.setBasename("classpath:messages");
+ messageSource.setDefaultEncoding("UTF-8");
+ return messageSource;
+ }
+
+ @Bean
+ public LocaleChangeInterceptor localeChangeInterceptor() {
+ LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
+ interceptor.setParamName("lang");
+ return interceptor;
+ }
+
// Add authUser to view model
private final HandlerInterceptor authInterceptor = new WebRequestHandlerInterceptorAdapter(new WebRequestInterceptor() {
@Override
@@ -56,6 +84,7 @@ public void preHandle(WebRequest request) {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor).excludePathPatterns("/api/**");
+ registry.addInterceptor(localeChangeInterceptor());
}
// http://www.codejava.net/frameworks/spring/spring-mvc-url-based-view-resolution-with-urlfilenameviewcontroller-example
@@ -79,6 +108,7 @@ ForwardedHeaderFilter forwardedHeaderFilter() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
+
}
@Override
diff --git a/src/main/java/com/javarush/jira/mail/MailService.java b/src/main/java/com/javarush/jira/mail/MailService.java
index 1b1623138..b224ba2b9 100644
--- a/src/main/java/com/javarush/jira/mail/MailService.java
+++ b/src/main/java/com/javarush/jira/mail/MailService.java
@@ -10,6 +10,7 @@
import com.javarush.jira.mail.internal.MailCaseRepository;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
+import jakarta.servlet.http.HttpServletRequest;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -19,6 +20,8 @@
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring6.SpringTemplateEngine;
@@ -29,7 +32,6 @@
@Service
@RequiredArgsConstructor
public class MailService {
- private static final Locale LOCALE_RU = Locale.forLanguageTag("ru");
private static final String OK = "OK";
private final MailCaseRepository mailCaseRepository;
@@ -50,20 +52,31 @@ public static boolean isOk(String result) {
return OK.equals(result);
}
+ private Locale determineUserLocale() {
+ HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+ String acceptLanguage = request.getHeader("Accept-Language");
+ if (acceptLanguage != null && !acceptLanguage.isEmpty()) {
+ return Locale.forLanguageTag(acceptLanguage.split(",")[0]);
+ }
+
+ return Locale.ENGLISH;
+ }
+
public String sendToUserWithParams(@NonNull String template, @NonNull User user, @NonNull Map params) {
String email = Objects.requireNonNull(user.getEmail());
Map extParams = Util.mergeMap(params, Map.of("user", user));
- return send(appConfig.isProd() ? email : appProperties.getTestMail(), user.getFirstName(), template, extParams);
+ Locale locale = determineUserLocale();
+ return send(appConfig.isProd() ? email : appProperties.getTestMail(), user.getFirstName(), template, extParams, locale);
}
- public String send(String toEmail, String toName, String template, Map params) {
+ public String send(String toEmail, String toName, String template, Map params, Locale locale) {
log.debug("Send email to {}, {} with template {}", toEmail, toName, template);
String result = OK;
try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");
message.setFrom(email, "JiraRush");
- String content = getContent(template, params);
+ String content = getContent(template, params, locale);
message.setText(content, true);
message.setSubject(Util.getTitle(content)); // TODO calculate title for group emailing only once
message.setTo(new InternetAddress(toEmail, toName, "UTF-8"));
@@ -78,8 +91,8 @@ public String send(String toEmail, String toName, String template, Map params) {
- Context context = new Context(LOCALE_RU, params);
+ private String getContent(String template, Map params, Locale locale) {
+ Context context = new Context(locale, params);
return templateEngine.process(template, context);
}
diff --git a/src/main/resources/application-secrets.yaml b/src/main/resources/application-secrets.yaml
new file mode 100644
index 000000000..beaf7793d
--- /dev/null
+++ b/src/main/resources/application-secrets.yaml
@@ -0,0 +1,46 @@
+spring:
+ datasource:
+ url: jdbc:postgresql://localhost:5432/jira
+ username: jira
+ password: JiraRush
+
+ security:
+ oauth2:
+ 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
+ scope: read_user
+ provider:
+ gitlab:
+ authorization-uri: https://gitlab.com/oauth/authorize
+ token-uri: https://gitlab.com/oauth/token
+ user-info-uri: https://gitlab.com/api/v4/user
+ user-name-attribute: email
+
+ mail:
+ properties:
+ mail:
+ smtp:
+ starttls:
+ enable: true
+ auth: true
+ host: smtp.gmail.com
+ username: jira4jr@gmail.com
+ password: zdfzsrqvgimldzyj
+ port: 587
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 7fcba1570..be8319782 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -8,6 +8,8 @@ app:
max-pool-size: 100
spring:
+ config:
+ import: optional:application-secrets.yaml
init:
mode: never
jpa:
@@ -25,10 +27,6 @@ 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
- datasource:
- url: jdbc:postgresql://localhost:5432/jira
- username: jira
- password: JiraRush
liquibase:
changeLog: "classpath:db/changelog.sql"
@@ -46,74 +44,10 @@ spring:
cache-names: users
caffeine.spec: maximumSize=10000,expireAfterAccess=5m
- security:
- oauth2:
- 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
- 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
- client-name: GitLab
- redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
- 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
- user-info-uri: https://gitlab.com/api/v4/user
- user-name-attribute: email
-
sql:
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
diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties
new file mode 100644
index 000000000..7ba8dd948
--- /dev/null
+++ b/src/main/resources/messages_en.properties
@@ -0,0 +1,8 @@
+email.confirmation.subject=JiraRush - Email Confirmation
+email.confirmation.greeting=Hello, {0}.
+email.confirmation.body=To complete setting up your account and start using JiraRush, please confirm your email.
+email.confirmation.link=Confirm Email
+home.title=JiraRush: mini bugtracking system
+home.logout=Logout
+home.language.en=English
+home.language.ru=Russian
\ No newline at end of file
diff --git a/src/main/resources/messages_ru.properties b/src/main/resources/messages_ru.properties
new file mode 100644
index 000000000..5cb75f163
--- /dev/null
+++ b/src/main/resources/messages_ru.properties
@@ -0,0 +1,8 @@
+email.confirmation.subject=JiraRush - \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u043f\u043e\u0447\u0442\u044b
+email.confirmation.greeting=\u041f\u0440\u0438\u0432\u0435\u0442, {0}.
+email.confirmation.body=\u0427\u0442\u043e\u0431\u044b \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u0438 \u043d\u0430\u0447\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f JiraRush, \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435, \u0447\u0442\u043e \u0432\u044b \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u0432\u0430\u0448\u0443 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0443\u044e \u043f\u043e\u0447\u0442\u0443.
+email.confirmation.link=\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u043f\u043e\u0447\u0442\u0443
+home.title=JiraRush: \u043c\u0438\u043d\u0438 \u0431\u0430\u0433\u0442\u0440\u0435\u043a\u0435\u0440
+home.logout=\u0412\u044b\u0439\u0442\u0438
+home.language.en=\u0410\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0439
+home.language.ru=\u0420\u0443\u0441\u0441\u043a\u0438\u0439
\ No newline at end of file
diff --git a/src/test/java/com/javarush/jira/AbstractControllerTest.java b/src/test/java/com/javarush/jira/AbstractControllerTest.java
index 5981bae53..8fa9264de 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:changelog-test.sql", "classpath:data.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/resources/application-test.yaml b/src/test/resources/application-test.yaml
index 51137fd06..056d63a5d 100644
--- a/src/test/resources/application-test.yaml
+++ b/src/test/resources/application-test.yaml
@@ -1,8 +1,7 @@
spring.cache.type: none
spring:
- init:
- mode: always
datasource:
- url: jdbc:postgresql://localhost:5433/jira-test
- username: jira
- password: JiraRush
\ No newline at end of file
+ url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=TRUE
+ driver-class-name: org.h2.Driver
+ liquibase:
+ changeLog: "classpath:changelog-test.sql"
\ No newline at end of file
diff --git a/src/test/resources/changelog-test.sql b/src/test/resources/changelog-test.sql
new file mode 100644
index 000000000..d921d08d5
--- /dev/null
+++ b/src/test/resources/changelog-test.sql
@@ -0,0 +1,316 @@
+--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),
+ ('vk', 'VK', 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);
+
+drop index UK_USER_BELONG;
\ No newline at end of file
diff --git a/src/test/resources/data.sql b/src/test/resources/data.sql
index 5087dbddc..5a1c52922 100644
--- a/src/test/resources/data.sql
+++ b/src/test/resources/data.sql
@@ -8,25 +8,25 @@ from PROFILE;
delete
from ACTIVITY;
-alter
-sequence ACTIVITY_ID_SEQ restart with 1;
+alter table ACTIVITY
+ alter column ID restart with 1;
delete
from TASK;
-alter
-sequence TASK_ID_SEQ restart with 1;
+alter table TASK
+ alter column ID restart with 1;
delete
from SPRINT;
-alter
-sequence SPRINT_ID_SEQ restart with 1;
+alter table SPRINT
+ alter column ID restart with 1;
delete
from PROJECT;
-alter
-sequence PROJECT_ID_SEQ restart with 1;
+alter table PROJECT
+ alter column ID restart with 1;
delete
from USERS;
-alter
-sequence USERS_ID_SEQ restart with 1;
+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'),
@@ -48,7 +48,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'),
@@ -95,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