From dd78aa956907f947264828cc1c375a75581b1509 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Wed, 5 Oct 2022 14:49:27 -0700 Subject: [PATCH 01/34] Update versions for hotfix --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 36050fac..5cdb9872 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ ubc.pavlab rdp - 1.5.3 + 1.5.4 Registry for model organism researchers, developed for the Canadian Rare Disease Models & Mechanisms Network. From 34fabccc3a83900dc0ebc70ba9b4c6c2fb0ed893 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Wed, 5 Oct 2022 14:50:30 -0700 Subject: [PATCH 02/34] docs: Fix latest JAR link --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index 7b9d1584..8f3bacc5 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -10,7 +10,7 @@ This section describes the essential steps to deploy an RDP registry. ## Obtain a distribution of RDP -Download the [latest JAR distribution](https://github.com/PavlidisLab/rdp/releases/latest) from GitHub. +Download the [JAR distribution from GitHub](https://github.com/PavlidisLab/rdp/releases/v{{ config.extra.rdp_version }}). ```bash wget https://github.com/PavlidisLab/rdp/releases/download/v{{ config.extra.rdp_version }}/rdp-{{ config.extra.rdp_version }}.jar From 67ed1da8e93035588ec917bd32b0dc47259d2143 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Thu, 20 Oct 2022 09:54:01 -0700 Subject: [PATCH 03/34] Make the welcome text on search page only appear to unregistered users --- src/main/resources/templates/search.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/resources/templates/search.html b/src/main/resources/templates/search.html index ac737c9b..3e0f7abd 100644 --- a/src/main/resources/templates/search.html +++ b/src/main/resources/templates/search.html @@ -1,6 +1,7 @@ @@ -17,7 +18,7 @@ ... -

...

+

...

From 920e896afe31ca2b468e3cefd2ebcacef30e327d Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Thu, 20 Oct 2022 09:54:47 -0700 Subject: [PATCH 04/34] Use isAnonymous() instead of !isAuthenticated() in templates --- src/main/resources/templates/layouts/common.html | 6 +++--- src/main/resources/templates/layouts/login.html | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/templates/layouts/common.html b/src/main/resources/templates/layouts/common.html index 445d6655..af5f3eb0 100644 --- a/src/main/resources/templates/layouts/common.html +++ b/src/main/resources/templates/layouts/common.html @@ -64,7 +64,7 @@ Search - -rdp.faq.answers.isearch=

The ${rdp.site.shortname} Network has recently established linkages with similar rare disease networks around the world, \ +rdp.faq.answers.isearch=

The {0} Network has recently established linkages with similar rare disease networks around the world, \ with the intention to enable matches across borders when there is no MO expertise within the country where the disease variant is discovered. \ We have shared the registry software with these partner networks and implemented changes so that these different software instances \ can exchange data in a secure way. \

\ -Partner registry sharing section on your Profile page gives you the option to set your preference for sharing your data with the ${rdp.site.shortname} Network's \ +Partner registry sharing section on your Profile page gives you the option to set your preference for sharing your data with the {0} Network's \ partner registries for the purposes of establishing connections between clinician sciences who are submitting their rare \ disease genes to partner registries and Canadian model organism researchers. \ The shared data will only be visible to the administrators of the partner registries. \ @@ -151,7 +151,7 @@ system to study candidate genes as would be required to \ contribute to the understanding of the identified human \ disease genes. This might include biochemical assays or assays \ based on cell lines. New research on human subjects is not \ -considered to be within the scope of ${rdp.site.shortname} Catalyst Grant projects. \ +considered to be within the scope of {0} Catalyst Grant projects. \ Do not register human genes merely because they are orthologs \ of the genes you study in one of the supported model organism.\ For organisms that are not currently supported in the registry see What if the organism I study is not listed?. diff --git a/src/main/resources/templates/user/faq.html b/src/main/resources/templates/user/faq.html index c72132b9..edecf407 100644 --- a/src/main/resources/templates/user/faq.html +++ b/src/main/resources/templates/user/faq.html @@ -18,17 +18,18 @@

-

+

-

+

From d854d69c793e5067c247c17fd5e72b775bd5c5fa Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Thu, 20 Oct 2022 12:40:07 -0700 Subject: [PATCH 07/34] Fix bottom margin for questions and answers in faq.html --- src/main/resources/templates/user/faq.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/resources/templates/user/faq.html b/src/main/resources/templates/user/faq.html index edecf407..92889116 100644 --- a/src/main/resources/templates/user/faq.html +++ b/src/main/resources/templates/user/faq.html @@ -26,10 +26,11 @@

+ th:text="${@messageSource.getMessage(@faqSettings.getResolvableQuestion(question.key), #locale)}"> + class="mb-0">
-
-

+
From 5befec61e79a022b38adf1220d030511122516c9 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Fri, 21 Oct 2022 09:28:38 -0700 Subject: [PATCH 08/34] Provide alternatives to deprecated rdp.settings.isearch.request-timeout setting in warning message --- src/main/java/ubc/pavlab/rdp/RemoteResourceConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ubc/pavlab/rdp/RemoteResourceConfig.java b/src/main/java/ubc/pavlab/rdp/RemoteResourceConfig.java index a3aa6d7f..c97d73a8 100644 --- a/src/main/java/ubc/pavlab/rdp/RemoteResourceConfig.java +++ b/src/main/java/ubc/pavlab/rdp/RemoteResourceConfig.java @@ -17,7 +17,7 @@ public AsyncRestTemplate remoteResourceRestTemplate( ApplicationSettings applica SimpleClientHttpRequestFactory httpRequestFactory = new SimpleClientHttpRequestFactory(); httpRequestFactory.setTaskExecutor( new SimpleAsyncTaskExecutor() ); if ( applicationSettings.getIsearch().getRequestTimeout() != null ) { - log.warn( "The 'rdp.settings.isearch.request-timeout' configuration is deprecated." ); + log.warn( "The 'rdp.settings.isearch.request-timeout' configuration is deprecated, use 'rdp.settings.isearch.connect-timeout' and 'rdp.settings.isearch.read-timeout' instead." ); httpRequestFactory.setConnectTimeout( 1000 ); httpRequestFactory.setReadTimeout( (int) applicationSettings.getIsearch().getRequestTimeout().toMillis() ); } else { From f3e9bf88f12b685ea55dfdbb7904c57698b8c49a Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Fri, 21 Oct 2022 09:43:01 -0700 Subject: [PATCH 09/34] Fix ordering of FAQ entries Using a LinkedHashMap is insufficient to guarantee the ordering of keys as they are first inserted in a java.util.Properties mapping. Instead, introduce a new `rdp.faq.keys` setting that specify the ordering of the FAQ entries. Add user documentation regarding that feature and also how to translate these messages. --- docs/customization.md | 23 +++++++++-- .../ubc/pavlab/rdp/settings/FaqSettings.java | 41 ++++++++++++++++--- src/main/resources/faq.properties | 4 ++ src/main/resources/templates/user/faq.html | 12 +++--- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/docs/customization.md b/docs/customization.md index e10b3150..84bc3317 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -520,13 +520,30 @@ In the file, each entry requires two parts: `rdp.faq.questions.` and `rdp question and the corresponding answer, respectively. ```properties +rdp.faq.keys= rdp.faq.questions.=A relevant question. rdp.faq.answers.=A plausible answer. ``` -The provided default file can be found -in [faq.properties](https://github.com/PavlidisLab/rdp/tree/{{ config.extra.git_ref -}}/src/main/resources/faq.properties). +The provided default file can be found in [faq.properties](https://github.com/PavlidisLab/rdp/tree/{{ config.extra.git_ref }}/src/main/resources/faq.properties). + +### Ordering FAQ entries + +!!! note + + New in 1.5.4 + +he `rdp.faq.keys` FAQ setting can be used to customize the ordering and appearance of FAQ entries. If the setting is +left unset, the default will be to arrange FAQ questions in alphabetic order of their keys. + +### Translating FAQ entries + +!!! note + + New in 1.5.4 + +The FAQ messages can now be translated by reusing the `rdp.faq.questions.` and `rdp.faq.answers.` as codes +for the `messages.properties` file. ## Terms of service and privacy policy diff --git a/src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java b/src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java index af7e60bb..ed87a67f 100644 --- a/src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java +++ b/src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java @@ -1,18 +1,22 @@ package ubc.pavlab.rdp.settings; import lombok.Data; +import lombok.extern.apachecommons.CommonsLog; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceResolvable; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.core.io.Resource; +import org.springframework.util.Assert; import ubc.pavlab.rdp.util.Messages; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.SortedMap; +import java.util.*; +import java.util.stream.Collectors; /** * Created by mjacobson on 22/01/18. @@ -21,13 +25,38 @@ @ConfigurationProperties(prefix = "rdp.faq") @PropertySource("${rdp.settings.faq-file}") @Data -public class FaqSettings { +@CommonsLog +public class FaqSettings implements InitializingBean { @Autowired private MessageSource messageSource; - private LinkedHashMap questions; - private LinkedHashMap answers; + @Value("${rdp.settings.faq-file}") + private Resource faqFile; + + private List keys; + private Map questions; + private Map answers; + + @Override + public void afterPropertiesSet() { + if ( keys == null ) { + log.warn( "The 'faq.keys' is unset, will default to the question keys in alphabetic order." ); + keys = questions.keySet().stream().sorted().collect( Collectors.toList() ); + } + List missingEntries = new ArrayList<>(); + for ( String key : keys ) { + if ( !questions.containsKey( key ) ) { + missingEntries.add( "faq.questions." + key ); + } + if ( !answers.containsKey( key ) ) { + missingEntries.add( "faq.answers." + key ); + } + } + Assert.isTrue( missingEntries.isEmpty(), String.format( "The following entries are missing in %s: %s.", + faqFile, + String.join( ", ", missingEntries ) ) ); + } public MessageSourceResolvable getResolvableQuestion( String key ) { return new DefaultMessageSourceResolvable( new String[]{ "rdp.faq.questions." + key }, new Object[]{ Messages.SHORTNAME }, questions.get( key ) ); diff --git a/src/main/resources/faq.properties b/src/main/resources/faq.properties index fe371b6d..494ea473 100644 --- a/src/main/resources/faq.properties +++ b/src/main/resources/faq.properties @@ -3,6 +3,10 @@ # = FAQ Questions & Answers # ============================================================== +# Identify which entries appear in which order in the FAQ +# If unset, the default is to arrange entries by alphabetic order of the key +rdp.faq.keys=what,purpose,who,privacy,privacy-settings,isearch,whatnow,process,organisms,genes,more,human,missing,ortholog,genome + rdp.faq.questions.what=What is the registry? rdp.faq.questions.purpose=What is the purpose of the registry? rdp.faq.questions.who=Who should register? diff --git a/src/main/resources/templates/user/faq.html b/src/main/resources/templates/user/faq.html index 92889116..4736542b 100644 --- a/src/main/resources/templates/user/faq.html +++ b/src/main/resources/templates/user/faq.html @@ -17,20 +17,20 @@
-
+
-

+

class="mb-0">

+ th:utext="${@messageSource.getMessage(@faqSettings.getResolvableAnswer(key), #locale)}">
From 3ba591ad8dbc527e97ea03844f8aceb23f16379a Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Fri, 21 Oct 2022 11:00:15 -0700 Subject: [PATCH 10/34] docs: Fix properties syntax for customizing term names and definitions --- docs/customization.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/customization.md b/docs/customization.md index 84bc3317..3af0c2de 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -318,10 +318,10 @@ The content of `messages.properties` has precedence over the values stored in th categories and terms. This allows you to override values that are imported from an OBO source as you see fit. ```properties -rdp.ontologies.{ontologyName}.title -rdp.ontologies.{ontologyName}.definition -rdp.ontologies.{ontologyName}.terms.{termName}.title -rdp.ontologies.{ontologyName}.terms.{termName}.definition +rdp.ontologies.{ontologyName}.title= +rdp.ontologies.{ontologyName}.definition= +rdp.ontologies.{ontologyName}.terms.{termName}.title= +rdp.ontologies.{ontologyName}.terms.{termName}.definition= ``` If your ontology/category is based on a [PURL](https://obofoundry.org/principles/fp-003-uris.html) source, its name will From 995045d3ba02392cf2e76fba6bddd7e5d3effdc4 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Fri, 21 Oct 2022 13:41:38 -0700 Subject: [PATCH 11/34] Use Messages.SHORTNAME --- .../pavlab/rdp/controllers/ApiController.java | 3 ++- .../pavlab/rdp/services/EmailServiceImpl.java | 25 ++++++++----------- .../pavlab/rdp/services/UserServiceImpl.java | 4 +-- .../java/ubc/pavlab/rdp/util/Messages.java | 2 +- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/main/java/ubc/pavlab/rdp/controllers/ApiController.java b/src/main/java/ubc/pavlab/rdp/controllers/ApiController.java index 45718d29..45bc361c 100644 --- a/src/main/java/ubc/pavlab/rdp/controllers/ApiController.java +++ b/src/main/java/ubc/pavlab/rdp/controllers/ApiController.java @@ -29,6 +29,7 @@ import ubc.pavlab.rdp.services.*; import ubc.pavlab.rdp.settings.ApplicationSettings; import ubc.pavlab.rdp.settings.SiteSettings; +import ubc.pavlab.rdp.util.Messages; import java.net.URI; import java.util.*; @@ -426,7 +427,7 @@ private void initUserTerm( UserOntologyTerm term, Locale locale ) { @SneakyThrows private T initRemoteResource( T remoteResource, Locale locale ) { - remoteResource.setOrigin( messageSource.getMessage( "rdp.site.shortname", null, locale ) ); + remoteResource.setOrigin( messageSource.getMessage( Messages.SHORTNAME, locale ) ); // Ensure that the path of the URL is effectively stripped from any trailing slashes and that its string // representation is free of query parameters, fragments, etc. // The main reason we do this is to avoid double slashes when generating URLs to profiles on partner sites diff --git a/src/main/java/ubc/pavlab/rdp/services/EmailServiceImpl.java b/src/main/java/ubc/pavlab/rdp/services/EmailServiceImpl.java index 87858174..13ff47a7 100644 --- a/src/main/java/ubc/pavlab/rdp/services/EmailServiceImpl.java +++ b/src/main/java/ubc/pavlab/rdp/services/EmailServiceImpl.java @@ -14,6 +14,7 @@ import ubc.pavlab.rdp.model.UserGene; import ubc.pavlab.rdp.model.VerificationToken; import ubc.pavlab.rdp.settings.SiteSettings; +import ubc.pavlab.rdp.util.Messages; import javax.mail.MessagingException; import javax.mail.internet.AddressException; @@ -88,8 +89,7 @@ private Future sendMultipartMessage( String subject, String content, Internet @Override public Future sendSupportMessage( String message, String name, User user, String userAgent, MultipartFile attachment, Locale locale ) throws MessagingException { InternetAddress replyTo = user.getVerifiedContactEmail().orElseThrow( () -> new MessagingException( "Could not find a verified email address for user." ) ); - String shortName = messageSource.getMessage( "rdp.site.shortname", new String[]{ siteSettings.getHostUrl().toString() }, Locale.getDefault() ); - String subject = messageSource.getMessage( "EmailService.sendSupportMessage.subject", new String[]{ shortName }, locale ); + String subject = messageSource.getMessage( "EmailService.sendSupportMessage.subject", new Object[]{ Messages.SHORTNAME }, locale ); String content = "Name: " + name + "\r\n" + "Email: " + user.getEmail() + "\r\n" + "User-Agent: " + userAgent + "\r\n" + @@ -124,8 +124,7 @@ public Future sendResetTokenMessage( User user, PasswordResetToken token, Loc // password reset always go through the primary email InternetAddress to = new InternetAddress( user.getEmail() ); - String shortName = messageSource.getMessage( "rdp.site.shortname", new String[]{ siteSettings.getHostUrl().toString() }, locale ); - String subject = messageSource.getMessage( "EmailService.sendResetTokenMessage.subject", new String[]{ shortName }, locale ); + String subject = messageSource.getMessage( "EmailService.sendResetTokenMessage.subject", new Object[]{ Messages.SHORTNAME }, locale ); String content = messageSource.getMessage( "EmailService.sendResetTokenMessage", new String[]{ user.getProfile().getName(), url.toString(), dateTimeFormatter.format( token.getExpiryDate().toInstant() ) }, locale ); @@ -134,12 +133,11 @@ public Future sendResetTokenMessage( User user, PasswordResetToken token, Loc @Override public Future sendRegistrationMessage( User user, VerificationToken token, Locale locale ) throws MessagingException { - String shortName = messageSource.getMessage( "rdp.site.shortname", new String[]{ siteSettings.getHostUrl().toString() }, locale ); - String registrationWelcome = messageSource.getMessage( "rdp.site.email.registration-welcome", new String[]{ siteSettings.getHostUrl().toString(), shortName }, locale ); + String registrationWelcome = messageSource.getMessage( "rdp.site.email.registration-welcome", new Object[]{ siteSettings.getHostUrl().toString(), Messages.SHORTNAME }, locale ); String registrationEnding = messageSource.getMessage( "rdp.site.email.registration-ending", new String[]{ siteSettings.getContactEmail() }, locale ); // registration always go through the primary email InternetAddress recipientAddress = new InternetAddress( user.getEmail() ); - String subject = messageSource.getMessage( "EmailService.sendRegistrationMessage.subject", new String[]{ shortName }, locale ); + String subject = messageSource.getMessage( "EmailService.sendRegistrationMessage.subject", new Object[]{ Messages.SHORTNAME }, locale ); URI confirmationUrl = UriComponentsBuilder.fromUri( siteSettings.getHostUrl() ) .path( "registrationConfirm" ) .queryParam( "token", "{token}" ) @@ -153,8 +151,7 @@ public Future sendRegistrationMessage( User user, VerificationToken token, Lo @Override public Future sendContactEmailVerificationMessage( User user, VerificationToken token, Locale locale ) throws MessagingException { InternetAddress recipientAddress = new InternetAddress( user.getProfile().getContactEmail() ); - String shortName = messageSource.getMessage( "rdp.site.shortname", new String[]{ siteSettings.getHostUrl().toString() }, locale ); - String subject = messageSource.getMessage( "EmailService.sendContactEmailVerificationMessage.subject", new String[]{ shortName }, locale ); + String subject = messageSource.getMessage( "EmailService.sendContactEmailVerificationMessage.subject", new Object[]{ Messages.SHORTNAME }, locale ); URI confirmationUrl = UriComponentsBuilder.fromUri( siteSettings.getHostUrl() ) .path( "user/verify-contact-email" ) .queryParam( "token", "{token}" ) @@ -167,8 +164,7 @@ public Future sendContactEmailVerificationMessage( User user, VerificationTok public Future sendUserRegisteredEmail( User user ) throws MessagingException { // unfortunately, there's no way to tell the dmin locale Locale locale = Locale.getDefault(); - String shortname = messageSource.getMessage( "rdp.site.shortname", null, locale ); - String subject = messageSource.getMessage( "EmailService.sendUserRegisteredEmail.subject", new String[]{ shortname }, locale ); + String subject = messageSource.getMessage( "EmailService.sendUserRegisteredEmail.subject", new Object[]{ Messages.SHORTNAME }, locale ); String content = messageSource.getMessage( "EmailService.sendUserRegisteredEmail", new String[]{ user.getEmail() }, locale ); return sendSimpleMessage( subject, content, getAdminAddress(), null, null ); } @@ -182,8 +178,7 @@ public Future sendUserGeneAccessRequest( UserGene userGene, User replyTo, Str InternetAddress replyToAddress = replyTo.getVerifiedContactEmail().orElseThrow( () -> new MessagingException( "Could not find a verified email address for user." ) ); // unfortunately, there's no way to tell the recipient locale for now Locale locale = Locale.getDefault(); - String shortname = messageSource.getMessage( "rdp.site.shortname", null, locale ); - String subject = messageSource.getMessage( "EmailService.sendUserGeneAccessRequest.subject", new String[]{ shortname }, locale ); + String subject = messageSource.getMessage( "EmailService.sendUserGeneAccessRequest.subject", new Object[]{ Messages.SHORTNAME }, locale ); String content = messageSource.getMessage( "EmailService.sendUserGeneAccessRequest", new String[]{ replyTo.getProfile().getFullName(), userGene.getSymbol(), reason, viewUserUrl.toString() }, locale ); return sendSimpleMessage( subject, content, to, replyToAddress, getAdminAddress() ); @@ -191,9 +186,9 @@ public Future sendUserGeneAccessRequest( UserGene userGene, User replyTo, Str private InternetAddress getAdminAddress() throws AddressException { try { - return new InternetAddress( siteSettings.getAdminEmail(), messageSource.getMessage( "rdp.site.shortname", null, Locale.getDefault() ) ); + return new InternetAddress( siteSettings.getAdminEmail(), messageSource.getMessage( Messages.SHORTNAME, Locale.getDefault() ) ); } catch ( UnsupportedEncodingException e ) { - log.error( "Could not encode the admin email personal, please set rdp.site.shortname correctly.", e ); + log.error( String.format( "Could not encode the admin email personal, please set %s correctly.", Messages.SHORTNAME.getCode() ), e ); return new InternetAddress( siteSettings.getAdminEmail() ); } } diff --git a/src/main/java/ubc/pavlab/rdp/services/UserServiceImpl.java b/src/main/java/ubc/pavlab/rdp/services/UserServiceImpl.java index b809e4f9..f535b2bb 100644 --- a/src/main/java/ubc/pavlab/rdp/services/UserServiceImpl.java +++ b/src/main/java/ubc/pavlab/rdp/services/UserServiceImpl.java @@ -40,6 +40,7 @@ import ubc.pavlab.rdp.settings.ApplicationSettings; import ubc.pavlab.rdp.util.CacheUtils; import ubc.pavlab.rdp.util.CollectionUtils; +import ubc.pavlab.rdp.util.Messages; import javax.validation.ValidationException; import java.security.SecureRandom; @@ -335,9 +336,8 @@ public User anonymizeUser( User user ) { @Override @PostAuthorize("hasPermission(returnObject, 'read')") public User anonymizeUser( User user, UUID anonymousId ) { - String shortName = messageSource.getMessage( "rdp.site.shortname", null, Locale.getDefault() ); Profile profile = Profile.builder() - .name( messageSource.getMessage( "rdp.site.anonymized-user-name", new String[]{ shortName }, Locale.getDefault() ) ) + .name( messageSource.getMessage( "rdp.site.anonymized-user-name", new Object[]{ Messages.SHORTNAME }, Locale.getDefault() ) ) .privacyLevel( applicationSettings.getPrivacy().isEnableAnonymizedSearchResults() ? PrivacyLevelType.PUBLIC : PrivacyLevelType.PRIVATE ) .shared( true ) .build(); diff --git a/src/main/java/ubc/pavlab/rdp/util/Messages.java b/src/main/java/ubc/pavlab/rdp/util/Messages.java index 19160495..73faf235 100644 --- a/src/main/java/ubc/pavlab/rdp/util/Messages.java +++ b/src/main/java/ubc/pavlab/rdp/util/Messages.java @@ -10,6 +10,6 @@ */ public final class Messages { - public static final MessageSourceResolvable SHORTNAME = new DefaultMessageSourceResolvable( "rdp.site.shortname" ); + public static final DefaultMessageSourceResolvable SHORTNAME = new DefaultMessageSourceResolvable( "rdp.site.shortname" ); public static final MessageSourceResolvable FULLNAME = new DefaultMessageSourceResolvable( "rdp.site.fullname" ); } From 3bc625992d6a5dbc0b8bc0b69515244b47d23ba4 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Sat, 22 Oct 2022 22:03:15 -0700 Subject: [PATCH 12/34] Use @TestPropertySource to fix controller-based tests Migrate controller tests to use JUnit 5 annotations so that @Nested can be used to configure group of tests once. These tests needs to be configured early as we now perform validation in afterPropertiesSet(), which run before JUnit setup the mocks. --- .../rdp/settings/ApplicationSettings.java | 2 +- .../ubc/pavlab/rdp/settings/FaqSettings.java | 2 +- .../ubc/pavlab/rdp/settings/SiteSettings.java | 2 +- .../rdp/controllers/AdminControllerTest.java | 24 +- .../rdp/controllers/ApiControllerTest.java | 336 ++++++++++-------- .../rdp/controllers/GeneControllerTest.java | 15 +- .../rdp/controllers/LoginControllerTest.java | 32 +- .../rdp/controllers/MainControllerTest.java | 46 +-- .../controllers/PasswordControllerTest.java | 15 +- .../rdp/controllers/SearchControllerTest.java | 54 +-- .../rdp/controllers/StatsControllerTest.java | 16 +- .../rdp/controllers/TermControllerTest.java | 15 +- .../rdp/controllers/UserControllerTest.java | 99 +++--- src/test/resources/application.properties | 15 +- src/test/resources/faq.properties | 3 + 15 files changed, 306 insertions(+), 370 deletions(-) create mode 100644 src/test/resources/faq.properties diff --git a/src/main/java/ubc/pavlab/rdp/settings/ApplicationSettings.java b/src/main/java/ubc/pavlab/rdp/settings/ApplicationSettings.java index 2273d261..630379b0 100644 --- a/src/main/java/ubc/pavlab/rdp/settings/ApplicationSettings.java +++ b/src/main/java/ubc/pavlab/rdp/settings/ApplicationSettings.java @@ -30,7 +30,7 @@ * Created by mjacobson on 22/01/18. */ @Validated -@Configuration +@Configuration("applicationSettings") @ConfigurationProperties(prefix = "rdp.settings") @Data public class ApplicationSettings { diff --git a/src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java b/src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java index ed87a67f..05bda04c 100644 --- a/src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java +++ b/src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java @@ -21,7 +21,7 @@ /** * Created by mjacobson on 22/01/18. */ -@Configuration +@Configuration("faqSettings") @ConfigurationProperties(prefix = "rdp.faq") @PropertySource("${rdp.settings.faq-file}") @Data diff --git a/src/main/java/ubc/pavlab/rdp/settings/SiteSettings.java b/src/main/java/ubc/pavlab/rdp/settings/SiteSettings.java index 10644b61..f451b832 100644 --- a/src/main/java/ubc/pavlab/rdp/settings/SiteSettings.java +++ b/src/main/java/ubc/pavlab/rdp/settings/SiteSettings.java @@ -16,7 +16,7 @@ * Created by mjacobson on 22/01/18. */ @Validated -@Configuration +@Configuration("siteSettings") @ConfigurationProperties(prefix = "rdp.site") @Data public class SiteSettings { diff --git a/src/test/java/ubc/pavlab/rdp/controllers/AdminControllerTest.java b/src/test/java/ubc/pavlab/rdp/controllers/AdminControllerTest.java index 46d04e25..651d136f 100644 --- a/src/test/java/ubc/pavlab/rdp/controllers/AdminControllerTest.java +++ b/src/test/java/ubc/pavlab/rdp/controllers/AdminControllerTest.java @@ -1,14 +1,14 @@ package ubc.pavlab.rdp.controllers; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.internal.verification.VerificationModeFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Import; import org.springframework.core.convert.converter.Converter; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ResourceLoader; @@ -24,6 +24,7 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -45,7 +46,6 @@ import ubc.pavlab.rdp.util.TestUtils; import java.io.*; -import java.net.URI; import java.net.URL; import java.time.Duration; import java.util.*; @@ -64,8 +64,9 @@ /** * Created by mjacobson on 13/02/18. */ -@RunWith(SpringRunner.class) @WebMvcTest(AdminController.class) +@TestPropertySource("classpath:application.properties") +@Import({ ApplicationSettings.class, SiteSettings.class }) public class AdminControllerTest { @Autowired @@ -95,15 +96,6 @@ public class AdminControllerTest { @MockBean(name = "userService") private UserService userService; - @MockBean(name = "applicationSettings") - private ApplicationSettings applicationSettings; - - @MockBean - ApplicationSettings.PrivacySettings privacySettings; - - @MockBean(name = "siteSettings") - private SiteSettings siteSettings; - @MockBean private PermissionEvaluator permissionEvaluator; @@ -150,9 +142,8 @@ public Ontology convert( String s ) { } } - @Before + @BeforeEach public void setUp() { - when( applicationSettings.getPrivacy() ).thenReturn( privacySettings ); when( ontologyService.resolveOntologyUrl( any( URL.class ) ) ).thenAnswer( a -> resourceLoader.getResource( a.getArgument( 0, URL.class ).toString() ) ); when( ontologyService.isSimple( any( Ontology.class ) ) ).thenAnswer( a -> a.getArgument( 0, Ontology.class ).getTerms().size() <= 20 ); conversionService.addConverter( new UserIdToUserConverter() ); @@ -179,7 +170,6 @@ public void getUsers() throws Exception { @Test @WithMockUser(roles = { "ADMIN" }) public void givenLoggedIn_whenCreateServiceAccount_thenRedirect3xx() throws Exception { - when( siteSettings.getHostUrl() ).thenReturn( URI.create( "http://localhost/" ) ); when( roleRepository.findById( 1 ) ).thenReturn( Optional.of( createRole( 1, "ROLE_USER" ) ) ); when( roleRepository.findById( 2 ) ).thenReturn( Optional.of( createRole( 2, "ROLE_ADMIN" ) ) ); when( userService.createServiceAccount( any() ) ).thenAnswer( answer -> { diff --git a/src/test/java/ubc/pavlab/rdp/controllers/ApiControllerTest.java b/src/test/java/ubc/pavlab/rdp/controllers/ApiControllerTest.java index 8d733e19..fe14cd6a 100644 --- a/src/test/java/ubc/pavlab/rdp/controllers/ApiControllerTest.java +++ b/src/test/java/ubc/pavlab/rdp/controllers/ApiControllerTest.java @@ -2,8 +2,10 @@ import org.assertj.core.util.Lists; import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.info.BuildProperties; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -20,7 +22,7 @@ import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; import ubc.pavlab.rdp.model.*; import ubc.pavlab.rdp.model.enums.PrivacyLevelType; @@ -31,6 +33,7 @@ import ubc.pavlab.rdp.services.*; import ubc.pavlab.rdp.settings.ApplicationSettings; import ubc.pavlab.rdp.settings.SiteSettings; +import ubc.pavlab.rdp.util.Messages; import java.util.*; @@ -43,10 +46,14 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static ubc.pavlab.rdp.util.TestUtils.*; -@WebMvcTest(value = ApiController.class, - properties = { "rdp.site.mainsite=https://example.com" }) -@RunWith(SpringRunner.class) -@Import({ SiteSettings.class }) +@WebMvcTest(value = ApiController.class) +@TestPropertySource(value = "classpath:application.properties", properties = { + "rdp.settings.search.enabled-search-modes=BY_RESEARCHER,BY_GENE", + "rdp.settings.privacy.enable-anonymized-search-results=false", + "rdp.settings.isearch.enabled=true", + "rdp.site.mainsite=https://example.com" +}) +@Import(value = { ApplicationSettings.class, SiteSettings.class }) @EnableSpringDataWebSupport public class ApiControllerTest { @@ -68,14 +75,6 @@ public class ApiControllerTest { @MockBean private OrganInfoService organInfoService; @MockBean - private ApplicationSettings applicationSettings; - @MockBean - private ApplicationSettings.SearchSettings searchSettings; - @MockBean - private ApplicationSettings.InternationalSearchSettings iSearchSettings; - @MockBean - private ApplicationSettings.PrivacySettings privacySettings; - @MockBean private UserDetailsService userDetailsService; @MockBean private PermissionEvaluator permissionEvaluator; @@ -86,16 +85,11 @@ public class ApiControllerTest { @MockBean private BuildProperties buildProperties; - @Before + @BeforeEach public void setUp() { - when( applicationSettings.getSearch() ).thenReturn( searchSettings ); - when( applicationSettings.getIsearch() ).thenReturn( iSearchSettings ); - when( applicationSettings.getPrivacy() ).thenReturn( privacySettings ); - when( searchSettings.getEnabledSearchModes() ).thenReturn( new LinkedHashSet<>( EnumSet.allOf( ApplicationSettings.SearchSettings.SearchMode.class ) ) ); - when( iSearchSettings.isEnabled() ).thenReturn( true ); - when( messageSource.getMessage( eq( "rdp.site.shortname" ), any(), any() ) ).thenReturn( "RDMM" ); when( userService.getRemoteSearchUser() ).thenReturn( Optional.empty() ); when( buildProperties.getVersion() ).thenReturn( "1.5.0" ); + when( messageSource.getMessage( eq( Messages.SHORTNAME ), any() ) ).thenReturn( "RDMM" ); } @Test @@ -135,9 +129,11 @@ public void searchUsers() throws Exception { User user = createUser( 1 ); when( userService.findByNameAndDescription( "robert", false, "pancake", null, null, null, null ) ) .thenReturn( Collections.singletonList( user ) ); + when( messageSource.getMessage( Messages.SHORTNAME, Locale.getDefault() ) ).thenReturn( "RDMM" ); mvc.perform( get( "/api/users/search" ) .param( "nameLike", "robert" ) - .param( "descriptionLike", "pancake" ) ) + .param( "descriptionLike", "pancake" ) + .locale( Locale.getDefault() ) ) .andExpect( status().isOk() ) .andExpect( jsonPath( "$[0].id" ).value( 1 ) ) .andExpect( jsonPath( "$[0].origin" ).value( "RDMM" ) ) @@ -182,71 +178,109 @@ public void searchUsers_whenTermDoesNotExistByOntologyDoes_thenReturnEmptyList() verify( userService ).findByNameAndDescription( "", false, "", null, null, null, Collections.singletonMap( ontology, Collections.emptySet() ) ); } - @Test - public void searchGenes_withSearchDisabled_thenReturnServiceUnavailable() throws Exception { - // configure remote authentication - when( iSearchSettings.getAuthTokens() ).thenReturn( Collections.singletonList( "1234" ) ); - when( userService.getRemoteSearchUser() ).thenReturn( Optional.of( createUser( 1 ) ) ); - - when( iSearchSettings.isEnabled() ).thenReturn( false ); - mvc.perform( get( "/api/genes/search" ) - .header( "Authorization", "Bearer 1234" ) - .param( "symbol", "CDH1" ) - .param( "taxonId", "9606" ) - .param( "tier", "TIER1" ) ) - .andExpect( status().isServiceUnavailable() ); + @Nested + public class WithAuthTokenButWithoutInternationalSearchEnabled { + + @BeforeEach + public void setUp() { + applicationSettings.getIsearch().setEnabled( false ); + applicationSettings.getIsearch().setUserId( 1 ); + applicationSettings.getIsearch().setAuthTokens( Collections.singletonList( "1234" ) ); + } + + @AfterEach + public void tearDown() { + applicationSettings.getIsearch().setEnabled( true ); + applicationSettings.getIsearch().setUserId( null ); + applicationSettings.getIsearch().setAuthTokens( Collections.emptyList() ); + } + + @Test + public void searchGenes_withSearchDisabled_thenReturnServiceUnavailable() throws Exception { + // configure remote authentication + when( userService.getRemoteSearchUser() ).thenReturn( Optional.of( createUser( 1 ) ) ); + mvc.perform( get( "/api/genes/search" ) + .header( "Authorization", "Bearer 1234" ) + .param( "symbol", "CDH1" ) + .param( "taxonId", "9606" ) + .param( "tier", "TIER1" ) ) + .andExpect( status().isServiceUnavailable() ); + } } - @Test - public void searchGenes_withAuthToken_thenReturnSuccess() throws Exception { - // configure remote authentication - when( iSearchSettings.getAuthTokens() ).thenReturn( Collections.singletonList( "1234" ) ); - when( userService.getRemoteSearchUser() ).thenReturn( Optional.of( createUser( 1 ) ) ); - - // configure one search result - Taxon humanTaxon = createTaxon( 9606 ); - User user = createUser( 2 ); - GeneInfo cdh1GeneInfo = createGene( 1, humanTaxon ); - UserGene cdh1UserGene = createUserGene( 1, cdh1GeneInfo, user, TierType.TIER1, PrivacyLevelType.PRIVATE ); - when( taxonService.findById( 9606 ) ).thenReturn( humanTaxon ); - when( geneService.findBySymbolAndTaxon( "CDH1", humanTaxon ) ).thenReturn( cdh1GeneInfo ); - when( userGeneService.handleGeneSearch( cdh1GeneInfo, EnumSet.of( TierType.TIER1 ), humanTaxon, null, null, null, null ) ) - .thenReturn( Lists.newArrayList( cdh1UserGene ) ); - - mvc.perform( get( "/api/genes/search" ) - .header( "Authorization", "Bearer 1234" ) - .param( "symbol", "CDH1" ) - .param( "taxonId", "9606" ) - .param( "tier", "TIER1" ) ) - .andExpect( status().is2xxSuccessful() ); - - verify( userService ).getRemoteSearchUser(); - } - - @Test - public void searchGenes_withAuthTokenInQuery_thenReturnSuccess() throws Exception { - // configure remote authentication - when( iSearchSettings.getAuthTokens() ).thenReturn( Collections.singletonList( "1234" ) ); - when( userService.getRemoteSearchUser() ).thenReturn( Optional.of( createUser( 1 ) ) ); - - // configure one search result - Taxon humanTaxon = createTaxon( 9606 ); - User user = createUser( 2 ); - GeneInfo cdh1GeneInfo = createGene( 1, humanTaxon ); - UserGene cdh1UserGene = createUserGene( 1, cdh1GeneInfo, user, TierType.TIER1, PrivacyLevelType.PRIVATE ); - when( taxonService.findById( 9606 ) ).thenReturn( humanTaxon ); - when( geneService.findBySymbolAndTaxon( "CDH1", humanTaxon ) ).thenReturn( cdh1GeneInfo ); - when( userGeneService.handleGeneSearch( cdh1GeneInfo, EnumSet.of( TierType.TIER1 ), humanTaxon, null, null, null, null ) ) - .thenReturn( Lists.newArrayList( cdh1UserGene ) ); - - mvc.perform( get( "/api/genes/search" ) - .param( "symbol", "CDH1" ) - .param( "taxonId", "9606" ) - .param( "tier", "TIER1" ) - .param( "auth", "1234" ) ) - .andExpect( status().is2xxSuccessful() ); - - verify( userService ).getRemoteSearchUser(); + @Nested + public class WithAuthToken { + + @BeforeEach + public void setUp() { + applicationSettings.getIsearch().setAuthTokens( Collections.singletonList( "1234" ) ); + } + + @AfterEach + public void tearDown() { + applicationSettings.getIsearch().setAuthTokens( Collections.emptyList() ); + } + + @Test + public void searchGenes_withAuthToken_thenReturnSuccess() throws Exception { + // configure remote authentication + when( userService.getRemoteSearchUser() ).thenReturn( Optional.of( createUser( 1 ) ) ); + + // configure one search result + Taxon humanTaxon = createTaxon( 9606 ); + User user = createUser( 2 ); + GeneInfo cdh1GeneInfo = createGene( 1, humanTaxon ); + UserGene cdh1UserGene = createUserGene( 1, cdh1GeneInfo, user, TierType.TIER1, PrivacyLevelType.PRIVATE ); + when( taxonService.findById( 9606 ) ).thenReturn( humanTaxon ); + when( geneService.findBySymbolAndTaxon( "CDH1", humanTaxon ) ).thenReturn( cdh1GeneInfo ); + when( userGeneService.handleGeneSearch( cdh1GeneInfo, EnumSet.of( TierType.TIER1 ), humanTaxon, null, null, null, null ) ) + .thenReturn( Lists.newArrayList( cdh1UserGene ) ); + + mvc.perform( get( "/api/genes/search" ) + .header( "Authorization", "Bearer 1234" ) + .param( "symbol", "CDH1" ) + .param( "taxonId", "9606" ) + .param( "tier", "TIER1" ) ) + .andExpect( status().is2xxSuccessful() ); + + verify( userService ).getRemoteSearchUser(); + } + + @Test + public void searchGenes_withAuthTokenInQuery_thenReturnSuccess() throws Exception { + // configure remote authentication + when( userService.getRemoteSearchUser() ).thenReturn( Optional.of( createUser( 1 ) ) ); + + // configure one search result + Taxon humanTaxon = createTaxon( 9606 ); + User user = createUser( 2 ); + GeneInfo cdh1GeneInfo = createGene( 1, humanTaxon ); + UserGene cdh1UserGene = createUserGene( 1, cdh1GeneInfo, user, TierType.TIER1, PrivacyLevelType.PRIVATE ); + when( taxonService.findById( 9606 ) ).thenReturn( humanTaxon ); + when( geneService.findBySymbolAndTaxon( "CDH1", humanTaxon ) ).thenReturn( cdh1GeneInfo ); + when( userGeneService.handleGeneSearch( cdh1GeneInfo, EnumSet.of( TierType.TIER1 ), humanTaxon, null, null, null, null ) ) + .thenReturn( Lists.newArrayList( cdh1UserGene ) ); + + mvc.perform( get( "/api/genes/search" ) + .param( "symbol", "CDH1" ) + .param( "taxonId", "9606" ) + .param( "tier", "TIER1" ) + .param( "auth", "1234" ) ) + .andExpect( status().is2xxSuccessful() ); + + verify( userService ).getRemoteSearchUser(); + } + + @Test + public void searchGenes_whenMisconfiguredRemoteAdmin_thenReturnUnauthorized() throws Exception { + mvc.perform( get( "/api/genes/search" ) + .header( "Authorization", "Bearer 1234" ) + .param( "symbol", "CDH1" ) + .param( "taxonId", "9606" ) + .param( "tier", "TIER1" ) ) + .andExpect( status().isUnauthorized() ) + .andExpect( content().contentType( MediaType.TEXT_PLAIN ) ); + } } @Test @@ -274,18 +308,6 @@ public void searchGenes_withInvalidAuthTokenScheme_thenIgnore() throws Exception .andExpect( content().contentType( MediaType.TEXT_PLAIN ) ); } - @Test - public void searchGenes_whenMisconfiguredRemoteAdmin_thenReturnUnauthorized() throws Exception { - when( iSearchSettings.getAuthTokens() ).thenReturn( Collections.singletonList( "1234" ) ); - mvc.perform( get( "/api/genes/search" ) - .header( "Authorization", "Bearer 1234" ) - .param( "symbol", "CDH1" ) - .param( "taxonId", "9606" ) - .param( "tier", "TIER1" ) ) - .andExpect( status().isUnauthorized() ) - .andExpect( content().contentType( MediaType.TEXT_PLAIN ) ); - } - @Test public void searchGenes_withSingleTier_thenReturn200() throws Exception { Taxon humanTaxon = createTaxon( 9606 ); @@ -439,34 +461,84 @@ public void searchGenes_withInvalidTier_thenReturnBadRequest() throws Exception public void getUser_thenReturn2xxSuccessful() throws Exception { User user = createUser( 1 ); when( userService.findUserById( 1 ) ).thenReturn( user ); - mvc.perform( get( "/api/users/{userId}", 1 ) ) + when( messageSource.getMessage( Messages.SHORTNAME, Locale.getDefault() ) ).thenReturn( "RDMM" ); + mvc.perform( get( "/api/users/{userId}", 1 ).locale( Locale.getDefault() ) ) .andExpect( status().is2xxSuccessful() ) .andExpect( jsonPath( "$.id" ).value( 1 ) ) .andExpect( jsonPath( "$.origin" ).value( "RDMM" ) ) .andExpect( jsonPath( "$.originUrl" ).value( "http://localhost" ) ); } - @Test - public void getUser_withAnonymousId_thenReturn2xxSuccessful() throws Exception { - User user = createUser( 1 ); - UUID anonymousId = UUID.randomUUID(); - when( applicationSettings.getPrivacy().isEnableAnonymizedSearchResults() ).thenReturn( true ); - when( userService.findUserByAnonymousIdNoAuth( anonymousId ) ).thenReturn( user ); - when( userService.anonymizeUser( user, anonymousId ) ).thenReturn( User.builder( new Profile() ).anonymousId( anonymousId ).build() ); - mvc.perform( get( "/api/users/by-anonymous-id/{anonymousId}", anonymousId ) ) - .andExpect( status().is2xxSuccessful() ) - .andExpect( jsonPath( "$.id" ).doesNotExist() ) - .andExpect( jsonPath( "$.anonymousId" ).value( anonymousId.toString() ) ) - .andExpect( jsonPath( "$.origin" ).value( "RDMM" ) ) - .andExpect( jsonPath( "$.originUrl" ).value( "http://localhost" ) ); - verify( userService ).anonymizeUser( user, anonymousId ); + @Autowired + private ApplicationSettings applicationSettings; + + @Nested + public class WithEnabledAnonymizedSearchResults { + + @BeforeEach + public void setUp() { + applicationSettings.getPrivacy().setEnableAnonymizedSearchResults( true ); + } + + @AfterEach + public void tearDown() { + applicationSettings.getPrivacy().setEnableAnonymizedSearchResults( false ); + } + + @Test + public void getUser_withAnonymousId_thenReturn2xxSuccessful() throws Exception { + User user = createUser( 1 ); + UUID anonymousId = UUID.randomUUID(); + when( userService.findUserByAnonymousIdNoAuth( anonymousId ) ).thenReturn( user ); + when( userService.anonymizeUser( user, anonymousId ) ).thenReturn( User.builder( new Profile() ).anonymousId( anonymousId ).build() ); + mvc.perform( get( "/api/users/by-anonymous-id/{anonymousId}", anonymousId ) ) + .andExpect( status().is2xxSuccessful() ) + .andExpect( jsonPath( "$.id" ).doesNotExist() ) + .andExpect( jsonPath( "$.anonymousId" ).value( anonymousId.toString() ) ) + .andExpect( jsonPath( "$.origin" ).value( "RDMM" ) ) + .andExpect( jsonPath( "$.originUrl" ).value( "http://localhost" ) ); + verify( userService ).anonymizeUser( user, anonymousId ); + } + + @Test + public void getUserGene_withAnonymousId() throws Exception { + Gene gene = createGene( 1, createTaxon( 9606 ) ); + UserGene userGene = createUserGene( 1, gene, createUser( 1 ), TierType.TIER1, PrivacyLevelType.PRIVATE ); + assertThat( userGene.getEffectivePrivacyLevel() ).isEqualTo( PrivacyLevelType.PRIVATE ); + UUID anonymousId = UUID.randomUUID(); + when( userService.findUserGeneByAnonymousIdNoAuth( anonymousId ) ).thenReturn( userGene ); + when( userService.anonymizeUserGene( userGene, anonymousId ) ).thenReturn( UserGene.builder( User.builder( new Profile() ).build() ) + .anonymousId( anonymousId ) + .privacyLevel( PrivacyLevelType.PUBLIC ) + .build() ); + when( permissionEvaluator.hasPermission( any(), eq( userGene ), eq( Permissions.READ ) ) ).thenReturn( false ); + mvc.perform( get( "/api/genes/by-anonymous-id/{anonymousId}", anonymousId ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "$.id" ).doesNotExist() ) + .andExpect( jsonPath( "$.anonymousId" ).value( anonymousId.toString() ) ); + verify( userService ).findUserGeneByAnonymousIdNoAuth( anonymousId ); + verify( userService ).anonymizeUserGene( userGene, anonymousId ); + } + + @Test + public void getUserGeneByAnonymousId_whenUserCanSeeTheGene_thenReturnUnanonymizedUserGene() throws Exception { + Gene gene = createGene( 1, createTaxon( 9606 ) ); + UserGene userGene = createUserGene( 1, gene, createUser( 1 ), TierType.TIER1, PrivacyLevelType.PRIVATE ); + UUID anonymousId = UUID.randomUUID(); + when( userService.findUserGeneByAnonymousIdNoAuth( anonymousId ) ).thenReturn( userGene ); + when( userService.anonymizeUserGene( userGene ) ).thenReturn( UserGene.builder( User.builder( new Profile() ).build() ).anonymousId( anonymousId ).build() ); + when( permissionEvaluator.hasPermission( any(), eq( userGene ), eq( Permissions.READ ) ) ).thenReturn( true ); + mvc.perform( get( "/api/genes/by-anonymous-id/{anonymousId}", anonymousId ) ) + .andExpect( status().isOk() ); + verify( userService ).findUserGeneByAnonymousIdNoAuth( anonymousId ); + verifyNoMoreInteractions( userService ); + } } @Test public void getUser_withAnonymousIdAndFeatureIsDisabled_thenReturnServiceUnavailable() throws Exception { User user = createUser( 1 ); UUID anonymousId = UUID.randomUUID(); - when( applicationSettings.getPrivacy().isEnableAnonymizedSearchResults() ).thenReturn( false ); when( userService.findUserByAnonymousIdNoAuth( anonymousId ) ).thenReturn( user ); when( userService.anonymizeUser( user, anonymousId ) ).thenReturn( User.builder( new Profile() ).anonymousId( anonymousId ).build() ); mvc.perform( get( "/api/users/by-anonymous-id/{anonymousId}", anonymousId ) ) @@ -474,42 +546,6 @@ public void getUser_withAnonymousIdAndFeatureIsDisabled_thenReturnServiceUnavail verifyNoInteractions( userService ); } - @Test - public void getUserGene_withAnonymousId() throws Exception { - Gene gene = createGene( 1, createTaxon( 9606 ) ); - UserGene userGene = createUserGene( 1, gene, createUser( 1 ), TierType.TIER1, PrivacyLevelType.PRIVATE ); - assertThat( userGene.getEffectivePrivacyLevel() ).isEqualTo( PrivacyLevelType.PRIVATE ); - UUID anonymousId = UUID.randomUUID(); - when( userService.findUserGeneByAnonymousIdNoAuth( anonymousId ) ).thenReturn( userGene ); - when( userService.anonymizeUserGene( userGene, anonymousId ) ).thenReturn( UserGene.builder( User.builder( new Profile() ).build() ) - .anonymousId( anonymousId ) - .privacyLevel( PrivacyLevelType.PUBLIC ) - .build() ); - when( permissionEvaluator.hasPermission( any(), eq( userGene ), eq( Permissions.READ ) ) ).thenReturn( false ); - when( applicationSettings.getPrivacy().isEnableAnonymizedSearchResults() ).thenReturn( true ); - mvc.perform( get( "/api/genes/by-anonymous-id/{anonymousId}", anonymousId ) ) - .andExpect( status().isOk() ) - .andExpect( jsonPath( "$.id" ).doesNotExist() ) - .andExpect( jsonPath( "$.anonymousId" ).value( anonymousId.toString() ) ); - verify( userService ).findUserGeneByAnonymousIdNoAuth( anonymousId ); - verify( userService ).anonymizeUserGene( userGene, anonymousId ); - } - - @Test - public void getUserGeneByAnonymousId_whenUserCanSeeTheGene_thenReturnUnanonymizedUserGene() throws Exception { - Gene gene = createGene( 1, createTaxon( 9606 ) ); - UserGene userGene = createUserGene( 1, gene, createUser( 1 ), TierType.TIER1, PrivacyLevelType.PRIVATE ); - UUID anonymousId = UUID.randomUUID(); - when( userService.findUserGeneByAnonymousIdNoAuth( anonymousId ) ).thenReturn( userGene ); - when( userService.anonymizeUserGene( userGene ) ).thenReturn( UserGene.builder( User.builder( new Profile() ).build() ).anonymousId( anonymousId ).build() ); - when( permissionEvaluator.hasPermission( any(), eq( userGene ), eq( Permissions.READ ) ) ).thenReturn( true ); - when( applicationSettings.getPrivacy().isEnableAnonymizedSearchResults() ).thenReturn( true ); - mvc.perform( get( "/api/genes/by-anonymous-id/{anonymousId}", anonymousId ) ) - .andExpect( status().isOk() ); - verify( userService ).findUserGeneByAnonymousIdNoAuth( anonymousId ); - verifyNoMoreInteractions( userService ); - } - @Test public void createOntology_() { assertThat( createOntology( "uberon", 1, 3, 1.0 ).getTerms() ) diff --git a/src/test/java/ubc/pavlab/rdp/controllers/GeneControllerTest.java b/src/test/java/ubc/pavlab/rdp/controllers/GeneControllerTest.java index 3f630bb5..2c0827ab 100644 --- a/src/test/java/ubc/pavlab/rdp/controllers/GeneControllerTest.java +++ b/src/test/java/ubc/pavlab/rdp/controllers/GeneControllerTest.java @@ -1,15 +1,15 @@ package ubc.pavlab.rdp.controllers; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; import ubc.pavlab.rdp.model.GeneInfo; import ubc.pavlab.rdp.model.Taxon; @@ -31,8 +31,9 @@ import static ubc.pavlab.rdp.util.TestUtils.createGene; import static ubc.pavlab.rdp.util.TestUtils.createTaxon; -@RunWith(SpringRunner.class) @WebMvcTest(GeneController.class) +@TestPropertySource("classpath:application.properties") +@Import({ ApplicationSettings.class, SiteSettings.class }) public class GeneControllerTest { @Autowired @@ -50,12 +51,6 @@ public class GeneControllerTest { @MockBean private UserService userService; - @MockBean - private ApplicationSettings applicationSettings; - - @MockBean - private SiteSettings siteSettings; - @MockBean private UserDetailsService userDetailsService; diff --git a/src/test/java/ubc/pavlab/rdp/controllers/LoginControllerTest.java b/src/test/java/ubc/pavlab/rdp/controllers/LoginControllerTest.java index d95e45d3..571e4f76 100644 --- a/src/test/java/ubc/pavlab/rdp/controllers/LoginControllerTest.java +++ b/src/test/java/ubc/pavlab/rdp/controllers/LoginControllerTest.java @@ -1,13 +1,13 @@ package ubc.pavlab.rdp.controllers; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Import; import org.springframework.core.convert.converter.Converter; import org.springframework.format.support.FormattingConversionService; import org.springframework.http.MediaType; @@ -15,7 +15,7 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; import ubc.pavlab.rdp.exception.TokenException; import ubc.pavlab.rdp.model.Profile; @@ -37,8 +37,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static ubc.pavlab.rdp.util.TestUtils.createUser; -@RunWith(SpringRunner.class) @WebMvcTest(LoginController.class) +@TestPropertySource(value = "classpath:application.properties", properties = { + "rdp.site.contact-email=support@example.com" +}) +@Import({ ApplicationSettings.class, SiteSettings.class }) public class LoginControllerTest { @Autowired @@ -50,15 +53,6 @@ public class LoginControllerTest { @MockBean private PrivacyService privacyService; - @MockBean - private ApplicationSettings applicationSettings; - - @MockBean - private ApplicationSettings.PrivacySettings privacySettings; - - @MockBean(name = "siteSettings") - private SiteSettings siteSettings; - @MockBean private UserDetailsService userDetailsService; @@ -74,10 +68,8 @@ public class LoginControllerTest { @MockBean(name = "ontologyMessageSource") private MessageSource ontologyMessageSource; - @Before + @BeforeEach public void setUp() { - when( applicationSettings.getPrivacy() ).thenReturn( privacySettings ); - when( siteSettings.getContactEmail() ).thenReturn( "support@example.com" ); when( privacyService.getDefaultPrivacyLevel() ).thenReturn( PrivacyLevelType.PRIVATE ); } @@ -112,13 +104,11 @@ public void register_thenReturnSuccess() throws Exception { .andExpect( status().isOk() ) .andExpect( view().name( "registration" ) ) .andExpect( model().attribute( "user", new User() ) ); - - when( applicationSettings.getPrivacy() ).thenReturn( privacySettings ); when( userService.create( any() ) ).thenAnswer( answer -> answer.getArgument( 0, User.class ) ); } @Test - @Ignore("I have absolutely no idea why this converter does not work anymore. See https://github.com/PavlidisLab/rdp/issues/171 for details.") + @Disabled("I have absolutely no idea why this converter does not work anymore. See https://github.com/PavlidisLab/rdp/issues/171 for details.") public void register_whenEmailIsUsedButNotEnabled_thenResendConfirmation() throws Exception { User user = User.builder( new Profile() ) .email( "foo@example.com" ) diff --git a/src/test/java/ubc/pavlab/rdp/controllers/MainControllerTest.java b/src/test/java/ubc/pavlab/rdp/controllers/MainControllerTest.java index 7f1ebc97..15111638 100644 --- a/src/test/java/ubc/pavlab/rdp/controllers/MainControllerTest.java +++ b/src/test/java/ubc/pavlab/rdp/controllers/MainControllerTest.java @@ -2,32 +2,26 @@ import org.hamcrest.FeatureMatcher; import org.hamcrest.Matcher; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.MessageSource; import org.springframework.context.NoSuchMessageException; +import org.springframework.context.annotation.Import; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; import ubc.pavlab.rdp.model.User; -import ubc.pavlab.rdp.model.enums.ResearcherCategory; -import ubc.pavlab.rdp.model.enums.ResearcherPosition; -import ubc.pavlab.rdp.model.enums.TierType; import ubc.pavlab.rdp.security.Permissions; import ubc.pavlab.rdp.services.TaxonService; import ubc.pavlab.rdp.services.UserService; import ubc.pavlab.rdp.settings.ApplicationSettings; import ubc.pavlab.rdp.settings.SiteSettings; -import java.util.EnumSet; - import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.containsString; import static org.mockito.ArgumentMatchers.*; @@ -37,30 +31,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static ubc.pavlab.rdp.util.TestUtils.createUser; -@RunWith(SpringRunner.class) @WebMvcTest(MainController.class) +@TestPropertySource("classpath:application.properties") +@Import({ ApplicationSettings.class, SiteSettings.class }) public class MainControllerTest { @Autowired - MockMvc mvc; - - @MockBean(name = "applicationSettings") - private ApplicationSettings applicationSettings; - - @MockBean - private ApplicationSettings.ProfileSettings profileSettings; - - @MockBean - private ApplicationSettings.PrivacySettings privacySettings; - - @MockBean(name = "siteSettings") - private SiteSettings siteSettings; - - @MockBean - private ApplicationSettings.OrganSettings organSettings; - - @MockBean - private ApplicationSettings.InternationalSearchSettings iSearchSettings; + private MockMvc mvc; @MockBean(name = "userService") private UserService userService; @@ -80,17 +57,6 @@ public class MainControllerTest { @MockBean(name = "ontologyMessageSource") private MessageSource ontologyMessageSource; - @Before - public void setUp() { - when( applicationSettings.getEnabledTiers() ).thenReturn( EnumSet.allOf( TierType.class ) ); - when( applicationSettings.getPrivacy() ).thenReturn( privacySettings ); - when( applicationSettings.getProfile() ).thenReturn( profileSettings ); - when( profileSettings.getEnabledResearcherCategories() ).thenReturn( EnumSet.allOf( ResearcherCategory.class ) ); - when( profileSettings.getEnabledResearcherPositions() ).thenReturn( EnumSet.of( ResearcherPosition.PRINCIPAL_INVESTIGATOR ) ); - when( applicationSettings.getOrgans() ).thenReturn( organSettings ); - when( applicationSettings.getIsearch() ).thenReturn( iSearchSettings ); - } - @Test public void getIndex_redirect3xx() throws Exception { when( permissionEvaluator.hasPermission( any(), isNull(), eq( Permissions.SEARCH ) ) ).thenReturn( true ); diff --git a/src/test/java/ubc/pavlab/rdp/controllers/PasswordControllerTest.java b/src/test/java/ubc/pavlab/rdp/controllers/PasswordControllerTest.java index dd8e18fb..383437e0 100644 --- a/src/test/java/ubc/pavlab/rdp/controllers/PasswordControllerTest.java +++ b/src/test/java/ubc/pavlab/rdp/controllers/PasswordControllerTest.java @@ -1,17 +1,17 @@ package ubc.pavlab.rdp.controllers; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; import ubc.pavlab.rdp.model.PasswordReset; import ubc.pavlab.rdp.model.User; @@ -30,8 +30,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static ubc.pavlab.rdp.util.TestUtils.createUser; -@RunWith(SpringRunner.class) @WebMvcTest(PasswordController.class) +@TestPropertySource("classpath:application.properties") +@Import({ ApplicationSettings.class, SiteSettings.class }) public class PasswordControllerTest { @TestConfiguration @@ -51,9 +52,6 @@ public BCryptPasswordEncoder bCryptPasswordEncoder() { @Autowired private MockMvc mvc; - @MockBean(name = "siteSettings") - private SiteSettings siteSettings; - @MockBean private UserService userService; @@ -63,9 +61,6 @@ public BCryptPasswordEncoder bCryptPasswordEncoder() { @MockBean private UserRepository userRepository; - @MockBean - private ApplicationSettings applicationSettings; - @MockBean private PermissionEvaluator permissionEvaluator; diff --git a/src/test/java/ubc/pavlab/rdp/controllers/SearchControllerTest.java b/src/test/java/ubc/pavlab/rdp/controllers/SearchControllerTest.java index 5506cc8b..516b0880 100644 --- a/src/test/java/ubc/pavlab/rdp/controllers/SearchControllerTest.java +++ b/src/test/java/ubc/pavlab/rdp/controllers/SearchControllerTest.java @@ -2,20 +2,20 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.hamcrest.Matchers; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.internal.verification.VerificationModeFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; import ubc.pavlab.rdp.exception.RemoteException; import ubc.pavlab.rdp.exception.UnknownRemoteApiException; @@ -23,7 +23,6 @@ import ubc.pavlab.rdp.model.*; import ubc.pavlab.rdp.model.enums.PrivacyLevelType; import ubc.pavlab.rdp.model.enums.ResearcherCategory; -import ubc.pavlab.rdp.model.enums.ResearcherPosition; import ubc.pavlab.rdp.model.enums.TierType; import ubc.pavlab.rdp.model.ontology.Ontology; import ubc.pavlab.rdp.model.ontology.OntologyTermInfo; @@ -35,13 +34,17 @@ import java.io.IOException; import java.net.URI; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import java.util.stream.IntStream; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.*; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -49,7 +52,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static ubc.pavlab.rdp.util.TestUtils.*; -@RunWith(SpringRunner.class) +@TestPropertySource("classpath:application.properties") +@Import({ ApplicationSettings.class, SiteSettings.class }) @WebMvcTest({ SearchController.class, SearchViewController.class }) public class SearchControllerTest { @@ -59,30 +63,6 @@ public class SearchControllerTest { @Autowired private ObjectMapper objectMapper; - @MockBean(name = "applicationSettings") - private ApplicationSettings applicationSettings; - - @MockBean - private ApplicationSettings.ProfileSettings profileSettings; - - @MockBean - private ApplicationSettings.PrivacySettings privacySettings; - - @MockBean(name = "siteSettings") - private SiteSettings siteSettings; - - @MockBean - private ApplicationSettings.SearchSettings searchSettings; - - @MockBean - private ApplicationSettings.OrganSettings organSettings; - - @MockBean - private ApplicationSettings.InternationalSearchSettings iSearchSettings; - - @MockBean - private ApplicationSettings.OntologySettings ontologySettings; - @MockBean(name = "userService") private UserService userService; @@ -125,18 +105,8 @@ public class SearchControllerTest { @MockBean(name = "ontologyMessageSource") private MessageSource ontologyMessageSource; - @Before + @BeforeEach public void setUp() { - when( applicationSettings.getEnabledTiers() ).thenReturn( EnumSet.allOf( TierType.class ) ); - when( applicationSettings.getPrivacy() ).thenReturn( privacySettings ); - when( applicationSettings.getProfile() ).thenReturn( profileSettings ); - when( profileSettings.getEnabledResearcherCategories() ).thenReturn( EnumSet.allOf( ResearcherCategory.class ) ); - when( profileSettings.getEnabledResearcherPositions() ).thenReturn( EnumSet.of( ResearcherPosition.PRINCIPAL_INVESTIGATOR ) ); - when( applicationSettings.getSearch() ).thenReturn( searchSettings ); - when( searchSettings.getEnabledSearchModes() ).thenReturn( new LinkedHashSet<>( EnumSet.allOf( ApplicationSettings.SearchSettings.SearchMode.class ) ) ); - when( applicationSettings.getOrgans() ).thenReturn( organSettings ); - when( applicationSettings.getIsearch() ).thenReturn( iSearchSettings ); - when( applicationSettings.getOntology() ).thenReturn( ontologySettings ); when( permissionEvaluator.hasPermission( any(), isNull(), eq( Permissions.SEARCH ) ) ).thenReturn( true ); when( permissionEvaluator.hasPermission( any(), isNull(), eq( Permissions.INTERNATIONAL_SEARCH ) ) ).thenReturn( true ); } diff --git a/src/test/java/ubc/pavlab/rdp/controllers/StatsControllerTest.java b/src/test/java/ubc/pavlab/rdp/controllers/StatsControllerTest.java index 7bcfd9c0..b16396fa 100644 --- a/src/test/java/ubc/pavlab/rdp/controllers/StatsControllerTest.java +++ b/src/test/java/ubc/pavlab/rdp/controllers/StatsControllerTest.java @@ -1,7 +1,6 @@ package ubc.pavlab.rdp.controllers; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.info.BuildProperties; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -11,7 +10,6 @@ import org.springframework.http.HttpHeaders; import org.springframework.security.access.PermissionEvaluator; import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import ubc.pavlab.rdp.services.UserGeneService; import ubc.pavlab.rdp.services.UserService; @@ -23,22 +21,14 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -@TestPropertySource(properties = { - "rdp.site.host=localhost", - "rdp.site.contact-email=contact@localhost", - "rdp.site.admin-email=admin@localhost", - "rdp.site.mainsite=https://example.com" }) -@RunWith(SpringRunner.class) @WebMvcTest(StatsController.class) -@Import(SiteSettings.class) +@TestPropertySource("classpath:application.properties") +@Import({ ApplicationSettings.class, SiteSettings.class }) public class StatsControllerTest { @Autowired private MockMvc mvc; - @MockBean - private ApplicationSettings applicationSettings; - @MockBean private UserService userService; diff --git a/src/test/java/ubc/pavlab/rdp/controllers/TermControllerTest.java b/src/test/java/ubc/pavlab/rdp/controllers/TermControllerTest.java index 08138c44..5d84c1db 100644 --- a/src/test/java/ubc/pavlab/rdp/controllers/TermControllerTest.java +++ b/src/test/java/ubc/pavlab/rdp/controllers/TermControllerTest.java @@ -1,13 +1,15 @@ package ubc.pavlab.rdp.controllers; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Import; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import ubc.pavlab.rdp.model.GeneOntologyTermInfo; @@ -28,8 +30,9 @@ import static ubc.pavlab.rdp.util.TestUtils.createTaxon; import static ubc.pavlab.rdp.util.TestUtils.createTerm; -@RunWith(SpringRunner.class) @WebMvcTest(TermController.class) +@TestPropertySource("classpath:application.properties") +@Import({ ApplicationSettings.class, SiteSettings.class }) public class TermControllerTest { @Autowired @@ -41,12 +44,6 @@ public class TermControllerTest { @MockBean private GOService goService; - @MockBean - private ApplicationSettings applicationSettings; - - @MockBean - private SiteSettings siteSettings; - @MockBean private UserService userService; diff --git a/src/test/java/ubc/pavlab/rdp/controllers/UserControllerTest.java b/src/test/java/ubc/pavlab/rdp/controllers/UserControllerTest.java index 0f960cf5..812f86e8 100644 --- a/src/test/java/ubc/pavlab/rdp/controllers/UserControllerTest.java +++ b/src/test/java/ubc/pavlab/rdp/controllers/UserControllerTest.java @@ -7,34 +7,37 @@ import org.hamcrest.Matchers; import org.json.JSONArray; import org.json.JSONObject; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.mockito.internal.util.collections.Sets; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.MessageSource; -import org.springframework.core.io.ClassPathResource; +import org.springframework.context.annotation.Import; +import org.springframework.core.io.Resource; import org.springframework.http.MediaType; import org.springframework.mock.web.MockMultipartFile; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; import ubc.pavlab.rdp.exception.TokenException; import ubc.pavlab.rdp.model.*; -import ubc.pavlab.rdp.model.enums.*; +import ubc.pavlab.rdp.model.enums.Aspect; +import ubc.pavlab.rdp.model.enums.PrivacyLevelType; +import ubc.pavlab.rdp.model.enums.TierType; import ubc.pavlab.rdp.services.*; import ubc.pavlab.rdp.settings.ApplicationSettings; import ubc.pavlab.rdp.settings.FaqSettings; import ubc.pavlab.rdp.settings.SiteSettings; import java.util.ArrayList; -import java.util.EnumSet; import java.util.Locale; import java.util.Set; @@ -51,39 +54,22 @@ /** * Created by mjacobson on 13/02/18. */ -@RunWith(SpringRunner.class) @WebMvcTest(UserController.class) +@TestPropertySource(value = "classpath:application.properties", properties = { + "rdp.settings.faq-file=classpath:faq.properties", + "rdp.settings.enabled-tiers=TIER1,TIER2,TIER3", + "rdp.settings.ontology.enabled=true", + "rdp.settings.profile.enabled-researcher-categories=IN_SILICO,IN_VITRO_BIOCHEMICAL,IN_VITRO_CELLS,IN_VITRO_STRUCTURAL,IN_VIVO,OTHER", + "rdp.settings.profile.enabled-researcher-positions=PRINCIPAL_INVESTIGATOR" +}) +@Import({ ApplicationSettings.class, SiteSettings.class, FaqSettings.class }) public class UserControllerTest { @Autowired private MockMvc mvc; @Autowired - ObjectMapper objectMapper; - - @MockBean(name = "applicationSettings") - private ApplicationSettings applicationSettings; - - @MockBean - private ApplicationSettings.ProfileSettings profileSettings; - - @MockBean - private ApplicationSettings.PrivacySettings privacySettings; - - @MockBean(name = "siteSettings") - private SiteSettings siteSettings; - - @MockBean(name = "faqSettings") - private FaqSettings faqSettings; - - @MockBean - private ApplicationSettings.OrganSettings organSettings; - - @MockBean - private ApplicationSettings.InternationalSearchSettings iSearchSettings; - - @MockBean - private ApplicationSettings.OntologySettings ontologySettings; + private ObjectMapper objectMapper; @MockBean(name = "userService") private UserService userService; @@ -100,7 +86,7 @@ public class UserControllerTest { @MockBean private PrivacyService privacyService; - @MockBean + @MockBean(name = "organInfoService") private OrganInfoService organInfoService; // WebSecurityConfig @@ -121,17 +107,8 @@ public class UserControllerTest { @MockBean(name = "ontologyMessageSource") private MessageSource ontologyMessageSource; - @Before + @BeforeEach public void setUp() { - when( applicationSettings.getEnabledTiers() ).thenReturn( EnumSet.allOf( TierType.class ) ); - when( applicationSettings.getPrivacy() ).thenReturn( privacySettings ); - when( applicationSettings.getProfile() ).thenReturn( profileSettings ); - when( applicationSettings.getOntology() ).thenReturn( ontologySettings ); - when( ontologySettings.isEnabled() ).thenReturn( true ); - when( profileSettings.getEnabledResearcherCategories() ).thenReturn( EnumSet.allOf( ResearcherCategory.class ) ); - when( profileSettings.getEnabledResearcherPositions() ).thenReturn( EnumSet.of( ResearcherPosition.PRINCIPAL_INVESTIGATOR ) ); - when( applicationSettings.getOrgans() ).thenReturn( organSettings ); - when( applicationSettings.getIsearch() ).thenReturn( iSearchSettings ); when( taxonService.findById( any() ) ).then( i -> createTaxon( i.getArgument( 0, Integer.class ) ) ); when( userService.updateUserProfileAndPublicationsAndOrgansAndOntologyTerms( any(), any(), any(), any(), any(), any() ) ).thenAnswer( arg -> arg.getArgument( 0, User.class ) ); } @@ -179,7 +156,6 @@ public void getTermsGenesForTaxon_thenReturnSuccess() throws Exception { public void getUserStaticEndpoints_thenReturnSuccess() throws Exception { User user = createUser( 1 ); when( userService.findCurrentUser() ).thenReturn( user ); - when( applicationSettings.getFaqFile() ).thenReturn( new ClassPathResource( "faq.properties" ) ); mvc.perform( get( "/user/home" ) ) .andExpect( status().isOk() ) .andExpect( view().name( "user/home" ) ); @@ -191,13 +167,32 @@ public void getUserStaticEndpoints_thenReturnSuccess() throws Exception { .andExpect( view().name( "user/faq" ) ); } - @Test - @WithMockUser - public void getUserFaq_whenFaqIsDisabled_thenReturn404() throws Exception { - when( applicationSettings.getFaqFile() ).thenReturn( null ); - mvc.perform( get( "/user/faq" ) ) - .andExpect( status().isNotFound() ) - .andExpect( view().name( "error/404" ) ); + @Autowired + private ApplicationSettings applicationSettings; + + @Nested + public class WithoutFaq { + + private Resource originalFaq; + + @BeforeEach + public void setUp() { + originalFaq = applicationSettings.getFaqFile(); + applicationSettings.setFaqFile( null ); + } + + @AfterEach + public void tearDown() { + applicationSettings.setFaqFile( originalFaq ); + } + + @Test + @WithMockUser + public void getUserFaq_whenFaqIsDisabled_thenReturn404() throws Exception { + mvc.perform( get( "/user/faq" ) ) + .andExpect( status().isNotFound() ) + .andExpect( view().name( "error/404" ) ); + } } @Test diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index cdde688b..b6931c55 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -54,11 +54,20 @@ spring.jpa.properties.hibernate.show_sql=false # ============================================================== rdp.site.host=http://localhost +rdp.site.theme-color=#285187 rdp.site.contact-email=contact@localhost rdp.site.admin-email=admin@localhost +rdp.site.mainsite=https://example.com +# minimal settings rdp.settings.cache.enabled=false - -# Send email to admin-email whenever there is a new registration -rdp.settings.send-email-on-registration=false \ No newline at end of file +rdp.settings.faq-file= +rdp.settings.profile.enabled-researcher-positions= +rdp.settings.profile.enabled-researcher-categories= +rdp.settings.ontology.enabled=true +rdp.settings.organs.enabled=true +rdp.settings.enabled-tiers= +rdp.settings.privacy.enabled-gene-levels= +rdp.settings.search.enabled-search-modes=BY_GENE,BY_RESEARCHER +rdp.settings.isearch.auth-tokens= \ No newline at end of file diff --git a/src/test/resources/faq.properties b/src/test/resources/faq.properties new file mode 100644 index 00000000..9c086211 --- /dev/null +++ b/src/test/resources/faq.properties @@ -0,0 +1,3 @@ +rdp.faq.keys= +rdp.faq.questions.1= +rdp.faq.answers.1= From 77fc6b72530d75161fa36968b6fe28c74e661ef3 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Sat, 22 Oct 2022 22:26:21 -0700 Subject: [PATCH 13/34] Check if rpd.settings.faq-file is set before validating it --- src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java b/src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java index 05bda04c..46c9c188 100644 --- a/src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java +++ b/src/main/java/ubc/pavlab/rdp/settings/FaqSettings.java @@ -40,6 +40,8 @@ public class FaqSettings implements InitializingBean { @Override public void afterPropertiesSet() { + if ( faqFile == null ) + return; if ( keys == null ) { log.warn( "The 'faq.keys' is unset, will default to the question keys in alphabetic order." ); keys = questions.keySet().stream().sorted().collect( Collectors.toList() ); From d213962e02fd6a597f8999292efc75beabc7c3dd Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Sat, 22 Oct 2022 22:30:07 -0700 Subject: [PATCH 14/34] Update Spring Boot to 2.6.13 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5cdb9872..3716f5aa 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ org.springframework.boot spring-boot-starter-parent - 2.6.12 + 2.6.13 From 2dd43bf62c66b1c042ed46024ecabb6c46ccdb76 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Fri, 28 Oct 2022 11:29:36 -0700 Subject: [PATCH 15/34] Ignore synonyms that merely change case in saveFromObo (fix #296) --- .gitattributes | 1 + .../pavlab/rdp/services/OntologyService.java | 15 +++++++++++---- .../rdp/services/OntologyServiceTest.java | 18 ++++++++++++++++++ src/test/resources/cache/mondo2.obo | 3 +++ 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 src/test/resources/cache/mondo2.obo diff --git a/.gitattributes b/.gitattributes index 787080fe..f93be813 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,3 +19,4 @@ src/test/resources/cache/reactome_stable_ids.txt filter=lfs diff=lfs merge=lfs - src/test/resources/cache/nbo-base.obo filter=lfs diff=lfs merge=lfs -text src/main/resources/cache/DIOPT_filtered_data_Sept2022.gz filter=lfs diff=lfs merge=lfs -text src/main/resources/cache/DIOPT_filtered_data_May2021.gz filter=lfs diff=lfs merge=lfs -text +src/test/resources/cache/mondo2.obo filter=lfs diff=lfs merge=lfs -text diff --git a/src/main/java/ubc/pavlab/rdp/services/OntologyService.java b/src/main/java/ubc/pavlab/rdp/services/OntologyService.java index 51de4c1a..4eaead12 100644 --- a/src/main/java/ubc/pavlab/rdp/services/OntologyService.java +++ b/src/main/java/ubc/pavlab/rdp/services/OntologyService.java @@ -309,14 +309,21 @@ private Ontology saveFromObo( Ontology ontology, OBOParser.ParsingResult parsing t.getAltTermIds().clear(); t.getAltTermIds().addAll( term.getAltIds() ); - t.getSynonyms().clear(); + // FIXME: there's a bug in Hibernate (I think?) that insert duplicate records when a synonym is replaced with another of a different casing (see https://github.com/PavlidisLab/rdp/issues/296) + // This will unfortunately result in never updating synonyms with different casing + Set synonyms = new TreeSet<>( new OntologyTermInfo.SynonymComparator() ); for ( OBOParser.Term.Synonym s : term.getSynonyms() ) { - if ( s.getSynonym().length() > OntologyTermInfo.MAX_SYNONYM_LENGTH ) { + synonyms.add( s.getSynonym() ); + } + + t.getSynonyms().removeIf( s -> !synonyms.contains( s ) ); + for ( String s : synonyms ) { + if ( s.length() > OntologyTermInfo.MAX_SYNONYM_LENGTH ) { log.warn( String.format( "Synonym %s for term %s is too wide (%d characters) to fit in the database, it will be ignored.", - s, term, s.getSynonym().length() ) ); + s, term, s.length() ) ); continue; } - t.getSynonyms().add( s.getSynonym() ); + t.getSynonyms().add( s ); } t.setObsolete( term.getObsolete() != null && term.getObsolete() ); diff --git a/src/test/java/ubc/pavlab/rdp/services/OntologyServiceTest.java b/src/test/java/ubc/pavlab/rdp/services/OntologyServiceTest.java index 1323073a..af1b29fb 100644 --- a/src/test/java/ubc/pavlab/rdp/services/OntologyServiceTest.java +++ b/src/test/java/ubc/pavlab/rdp/services/OntologyServiceTest.java @@ -163,6 +163,24 @@ public void updateFromObo() throws IOException, ParseException, OntologyNameAlre assertThat( ontology.getTerms() ).hasSize( 14938 ); } + @Test + public void updateFromObo_whenSynonymChangeCase_thenIgnoreNewValue() throws OntologyNameAlreadyUsedException, IOException, ParseException { + Set synonyms; + + Ontology ontology = ontologySetupService.setupOntology( "mondo", false ); + // need to rehash because the sorted set is case insensitive + synonyms = new HashSet<>( ontologyService.findTermByTermIdAndOntology( "MONDO:0005296", ontology ).getSynonyms() ); + assertThat( synonyms ).contains( "sleep Apneas, mixed" ); + + // the casing has changed to "sleep apneas, mixed" in mondo2 + Resource resource = new ClassPathResource( "cache/mondo2.obo" ); + try ( Reader reader = new InputStreamReader( resource.getInputStream() ) ) { + ontologyService.updateFromObo( ontology, reader ); + } + synonyms = new HashSet<>( ontologyService.findTermByTermIdAndOntology( "MONDO:0005296", ontology ).getSynonyms() ); + assertThat( synonyms ).contains( "sleep Apneas, mixed" ); + } + @Test public void autocomplete() throws IOException, ParseException, OntologyNameAlreadyUsedException { Ontology ontology = ontologySetupService.setupOntology( "uberon", true ); diff --git a/src/test/resources/cache/mondo2.obo b/src/test/resources/cache/mondo2.obo new file mode 100644 index 00000000..368ef7b7 --- /dev/null +++ b/src/test/resources/cache/mondo2.obo @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff171fa9991e7fb11a15e7428d4de78c93570f0d34c15a3a1d22d36147f395f6 +size 29492991 From b2f18e047cbbc19ab0bc42c121bb7d43b0dc6c18 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Thu, 3 Nov 2022 13:13:00 -0700 Subject: [PATCH 16/34] Add missing filter by taoxn in findAllDistinctGeneIdByTaxon --- .../java/ubc/pavlab/rdp/repositories/UserGeneRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ubc/pavlab/rdp/repositories/UserGeneRepository.java b/src/main/java/ubc/pavlab/rdp/repositories/UserGeneRepository.java index d82fb5b5..e3789d03 100644 --- a/src/main/java/ubc/pavlab/rdp/repositories/UserGeneRepository.java +++ b/src/main/java/ubc/pavlab/rdp/repositories/UserGeneRepository.java @@ -104,7 +104,7 @@ public interface UserGeneRepository extends JpaRepository { * Only genes from enabled users are included. */ @QueryHints(@QueryHint(name = "org.hibernate.cacheable", value = "true")) - @Query(value = "select distinct ug.geneId from UserGene ug where ug.user.enabled = true") + @Query(value = "select distinct ug.geneId from UserGene ug where ug.user.enabled = true and ug.taxon = :taxon") Collection findAllDistinctGeneIdByTaxon( Taxon taxon ); /** From 71a9ec351a59f0d56071ebac92f5eb79d1886e3f Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Tue, 15 Nov 2022 13:05:19 -0800 Subject: [PATCH 17/34] Fix missing nullsLast() when ordering user by origin URL (fix #349) --- src/main/java/ubc/pavlab/rdp/model/User.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ubc/pavlab/rdp/model/User.java b/src/main/java/ubc/pavlab/rdp/model/User.java index 986768d5..52c9a535 100644 --- a/src/main/java/ubc/pavlab/rdp/model/User.java +++ b/src/main/java/ubc/pavlab/rdp/model/User.java @@ -67,7 +67,7 @@ public static UserBuilder builder( Profile profile ) { public static Comparator getComparator() { return Comparator .comparing( ( User u ) -> u.getProfile().getFullName() ) - .thenComparing( User::getOriginUrl ) + .thenComparing( User::getOriginUrl, Comparator.nullsLast( Comparator.naturalOrder() ) ) // at least one of the two must be non-null .thenComparing( User::getId, Comparator.nullsLast( Comparator.naturalOrder() ) ) .thenComparing( User::getAnonymousId, Comparator.nullsLast( Comparator.naturalOrder() ) ); From 3025c1b57cd84875b7402a520ec01df1bf9cc079 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Wed, 16 Nov 2022 11:29:48 -0800 Subject: [PATCH 18/34] Fix buggy timestamp default value with MySQL 5.7 --- .../java/ubc/pavlab/rdp/MigrationConfig.java | 50 +++++++++++++------ ...1.5.0.4__add_various_timestamp_columns.sql | 22 ++++---- 2 files changed, 45 insertions(+), 27 deletions(-) diff --git a/src/main/java/ubc/pavlab/rdp/MigrationConfig.java b/src/main/java/ubc/pavlab/rdp/MigrationConfig.java index af423db9..9cdeb299 100644 --- a/src/main/java/ubc/pavlab/rdp/MigrationConfig.java +++ b/src/main/java/ubc/pavlab/rdp/MigrationConfig.java @@ -21,26 +21,43 @@ @CommonsLog public class MigrationConfig { + @RequiredArgsConstructor + private enum NewChecksumReason { + /** + * A migration applied with Flyway 3. + *

+ * The checksum algorithm has changed. + */ + FLYWAY_3( "has been performed with Flyway 3.2.1" ), + /** + * A migration that introduced a regression with MySQL 5.7. + */ + MYSQL_5_7_REGRESSION( "introduced a regression with MySQL 5.7" ); + final String reason; + } + /** * Expected migration information that are used to determine if a repair is necessary. */ @RequiredArgsConstructor private static class ExpectedMigrationInfo { private final String version; - private final int pre15Checksum; - private final int post15Checksum; + private final int preRepairChecksum; + private final int postRepairChecksum; + private final NewChecksumReason newChecksumReason; } private static final ExpectedMigrationInfo[] MIGRATION_META = { - new ExpectedMigrationInfo( "1.0.0", -330642568, 1889522940 ), - new ExpectedMigrationInfo( "1.3.2", 1109324745, 1109324745 ), - new ExpectedMigrationInfo( "1.4.0", 310023814, 1017485172 ), - new ExpectedMigrationInfo( "1.4.1", -1543189200, 1330781885 ), - new ExpectedMigrationInfo( "1.4.2", 1706447069, 253128706 ), - new ExpectedMigrationInfo( "1.4.3", 571489108, 571489108 ), - new ExpectedMigrationInfo( "1.4.6", 399726006, 504755998 ), - new ExpectedMigrationInfo( "1.4.11", 1536441374, 709491229 ), - new ExpectedMigrationInfo( "1.4.11.1", -1312864724, -907949910 ) + new ExpectedMigrationInfo( "1.0.0", -330642568, 1889522940, NewChecksumReason.FLYWAY_3 ), + new ExpectedMigrationInfo( "1.3.2", 1109324745, 1109324745, NewChecksumReason.FLYWAY_3 ), + new ExpectedMigrationInfo( "1.4.0", 310023814, 1017485172, NewChecksumReason.FLYWAY_3 ), + new ExpectedMigrationInfo( "1.4.1", -1543189200, 1330781885, NewChecksumReason.FLYWAY_3 ), + new ExpectedMigrationInfo( "1.4.2", 1706447069, 253128706, NewChecksumReason.FLYWAY_3 ), + new ExpectedMigrationInfo( "1.4.3", 571489108, 571489108, NewChecksumReason.FLYWAY_3 ), + new ExpectedMigrationInfo( "1.4.6", 399726006, 504755998, NewChecksumReason.FLYWAY_3 ), + new ExpectedMigrationInfo( "1.4.11", 1536441374, 709491229, NewChecksumReason.FLYWAY_3 ), + new ExpectedMigrationInfo( "1.4.11.1", -1312864724, -907949910, NewChecksumReason.FLYWAY_3 ), + new ExpectedMigrationInfo( "1.5.0.4", 625408518, 1560455114, NewChecksumReason.MYSQL_5_7_REGRESSION ) }; /** @@ -74,15 +91,16 @@ public FlywayMigrationStrategy flywayMigrationStrategy() { .orElse( null ); if ( expectedMigration != null && appliedMigration.getChecksum() != null && - appliedMigration.getChecksum().equals( expectedMigration.pre15Checksum ) && - !appliedMigration.getChecksum().equals( expectedMigration.post15Checksum ) ) { - log.warn( String.format( "Flyway migration %s has been performed with Flyway 3.2.1, checksum will be bumped from %s to %s after repair.", - appliedMigration.getVersion(), appliedMigration.getChecksum(), expectedMigration.post15Checksum ) ); + appliedMigration.getChecksum().equals( expectedMigration.preRepairChecksum ) && + !appliedMigration.getChecksum().equals( expectedMigration.postRepairChecksum ) ) { repairNeeded = true; + log.warn( String.format( "Flyway migration %s %s, checksum will be bumped from %s to %s after repair.", + appliedMigration.getVersion(), expectedMigration.newChecksumReason.reason, + appliedMigration.getChecksum(), expectedMigration.postRepairChecksum ) ); } } if ( repairNeeded ) { - log.warn( "Flyway 3.2.1 migrations detected, Flyway repair will be performed." ); + log.warn( "Flyway repair will now be performed." ); flyway.repair(); } flyway.migrate(); diff --git a/src/main/resources/db/migration/common/V1.5.0.4__add_various_timestamp_columns.sql b/src/main/resources/db/migration/common/V1.5.0.4__add_various_timestamp_columns.sql index 563ab70e..8a7ff6a9 100644 --- a/src/main/resources/db/migration/common/V1.5.0.4__add_various_timestamp_columns.sql +++ b/src/main/resources/db/migration/common/V1.5.0.4__add_various_timestamp_columns.sql @@ -1,22 +1,22 @@ alter table user - add column created_at timestamp; + add column created_at timestamp null; alter table user - add column modified_at timestamp; + add column modified_at timestamp null; alter table user - add column enabled_at timestamp after enabled; + add column enabled_at timestamp null after enabled; alter table user - add column contact_email_verified_at timestamp after contact_email_verified; + add column contact_email_verified_at timestamp null after contact_email_verified; alter table gene - add column created_at timestamp; + add column created_at timestamp null; alter table user_ontology_term - add column created_at timestamp; + add column created_at timestamp null; alter table user_organ - add column created_at timestamp; + add column created_at timestamp null; alter table term - add column created_at timestamp; + add column created_at timestamp null; alter table access_token - add column created_at timestamp; + add column created_at timestamp null; alter table verification_token - add column created_at timestamp; + add column created_at timestamp null; alter table password_reset_token - add column created_at timestamp; \ No newline at end of file + add column created_at timestamp null; \ No newline at end of file From c52ccfbc5ed54175a8b47ef4fe8f240435b24d7e Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Wed, 16 Nov 2022 11:51:59 -0800 Subject: [PATCH 19/34] Add all supported databases to docker-compose.yml --- docker-compose.yml | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 40d60844..2db9c38a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,14 @@ +mysql57: + image: mysql:5.7 + ports: + - "3306:3306" + environment: + - MYSQL_USER=springuser + - MYSQL_PASSWORD=ThePassword + - MYSQL_DATABASE=db_example + - MYSQL_RANDOM_ROOT_PASSWORD=true mysql: - image: mysql + image: mysql:8.0 ports: - "3306:3306" environment: @@ -7,5 +16,12 @@ mysql: - MYSQL_PASSWORD=ThePassword - MYSQL_DATABASE=db_example - MYSQL_RANDOM_ROOT_PASSWORD=true - volumes: - - "./conf.d:/etc/mysql/conf.d:ro" \ No newline at end of file +mariadb: + image: mariadb:10.6 + ports: + - "3306:3306" + environment: + - MARIADB_USER=springuser + - MARIADB_PASSWORD=ThePassword + - MARIADB_DATABASE=db_example + - MARIADB_RANDOM_ROOT_PASSWORD=true From 9d4685e91a7a9f6da8456a915772df5e92386201 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Nov 2022 08:55:09 +0000 Subject: [PATCH 20/34] Bump loader-utils from 2.0.2 to 2.0.3 in /src/main/resources Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.2 to 2.0.3. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.3/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v2.0.2...v2.0.3) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/main/resources/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/resources/package-lock.json b/src/main/resources/package-lock.json index cf488eb4..bc55581e 100644 --- a/src/main/resources/package-lock.json +++ b/src/main/resources/package-lock.json @@ -3439,9 +3439,9 @@ } }, "node_modules/loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", + "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -7574,9 +7574,9 @@ "dev": true }, "loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", + "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", "dev": true, "requires": { "big.js": "^5.2.2", From a6d026063831dc045b234155e8d410a9eade872e Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Wed, 16 Nov 2022 11:56:01 -0800 Subject: [PATCH 21/34] Update Springdoc OpenAPI to 1.6.12 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3716f5aa..56cc2df4 100644 --- a/pom.xml +++ b/pom.xml @@ -113,7 +113,7 @@ org.springdoc springdoc-openapi-webmvc-core - 1.6.11 + 1.6.12 From 68268e611fb1b17e1cf4afce5d2e7df0eb9408fb Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Wed, 16 Nov 2022 11:57:11 -0800 Subject: [PATCH 22/34] Update rdp_version in docs --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 553922f0..6b0227a5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -14,5 +14,5 @@ plugins: markdown_extensions: - admonition extra: - rdp_version: 1.5.3 + rdp_version: 1.5.4 git_ref: master From f9ac371e86ba1955f3d12fddad29bb782f293941 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Wed, 16 Nov 2022 12:55:47 -0800 Subject: [PATCH 23/34] Add missing GA snippets in the login template and pass the User ID --- src/main/resources/templates/layouts/common.html | 4 +++- src/main/resources/templates/layouts/login.html | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/resources/templates/layouts/common.html b/src/main/resources/templates/layouts/common.html index af5f3eb0..3f8f96f1 100644 --- a/src/main/resources/templates/layouts/common.html +++ b/src/main/resources/templates/layouts/common.html @@ -158,7 +158,9 @@ } gtag('js', new Date()); - gtag('config', [[${@siteSettings.gaTracker}]]); + gtag('config', [[${@siteSettings.gaTracker}]], { + 'user_id': [[${@userService.findCurrentUser()?.id}]] + }); diff --git a/src/main/resources/templates/layouts/login.html b/src/main/resources/templates/layouts/login.html index a563f2af..ea527cbb 100644 --- a/src/main/resources/templates/layouts/login.html +++ b/src/main/resources/templates/layouts/login.html @@ -49,6 +49,22 @@

+ + + + + \ No newline at end of file From 263da0b231b032ac27fb127a33277ae39895f0b0 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Thu, 17 Nov 2022 11:37:27 -0800 Subject: [PATCH 24/34] Fix spring.jpa.properties.hibernate.id.new_generator_mappings in test configuration --- src/test/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index b6931c55..3a28dfb7 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -16,7 +16,7 @@ spring.datasource.tomcat.validation-query=SELECT 1 # =============================== spring.jpa.hibernate.ddl-auto=validate # Ignore the new scheme for generating id since auto-increment primary key is not supported with H2 -spring.jpa.hibernate.use-new-id-generator-mappings=false +spring.jpa.properties.hibernate.id.new_generator_mappings=false spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect spring.jpa.properties.hibernate.jdbc.batch_size=10 From 84f243c94e3fac08245d8586f1f41b4d7fa39e6a Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Thu, 17 Nov 2022 22:39:14 -0800 Subject: [PATCH 25/34] Update npm dependencies Track patch release for bootstrap and bootstrap-selector. Update all dev dependencies. --- src/main/resources/package-lock.json | 1795 ++++++++++++-------------- src/main/resources/package.json | 36 +- 2 files changed, 853 insertions(+), 978 deletions(-) diff --git a/src/main/resources/package-lock.json b/src/main/resources/package-lock.json index bc55581e..a17d94bb 100644 --- a/src/main/resources/package-lock.json +++ b/src/main/resources/package-lock.json @@ -7,28 +7,28 @@ "name": "rdp", "dependencies": { "activity-detector": "^3.0.0", - "bootstrap": "4.6.1", - "bootstrap-select": "1.13.14", - "datatables.net": "1.12.1", - "datatables.net-bs4": "1.12.1", - "jquery": "3.5.1", - "jquery-ui": "1.13.2", - "open-iconic": "1.1.1", - "popper.js": "1.16.1" + "bootstrap": "~4.6.2", + "bootstrap-select": "~1.13.18", + "datatables.net": "~1.12.1", + "datatables.net-bs4": "~1.12.1", + "jquery": "~3.5.1", + "jquery-ui": "~1.13.2", + "open-iconic": "~1.1.1", + "popper.js": "~1.16.1" }, "devDependencies": { - "@babel/core": "^7.18.10", - "@babel/preset-env": "^7.18.10", - "babel-loader": "^8.2.5", - "css-loader": "^6.7.1", - "postcss": "^8.4.16", + "@babel/core": "^7.20.2", + "@babel/preset-env": "^7.20.2", + "babel-loader": "^8.3.0", + "css-loader": "^6.7.2", + "postcss": "^8.4.19", "postcss-loader": "^7.0.1", - "postcss-preset-env": "^7.7.2", - "sass": "^1.54.4", - "sass-loader": "^13.0.2", - "source-map-loader": "^4.0.0", + "postcss-preset-env": "^7.8.3", + "sass": "^1.56.1", + "sass-loader": "^13.2.0", + "source-map-loader": "^4.0.1", "style-loader": "^3.3.1", - "webpack": "^5.74.0", + "webpack": "^5.75.0", "webpack-cli": "^4.10.0" } }, @@ -58,30 +58,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz", + "integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz", + "integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", + "@babel/generator": "^7.20.2", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.1", + "@babel/parser": "^7.20.2", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -97,12 +97,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.4.tgz", + "integrity": "sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA==", "dev": true, "dependencies": { - "@babel/types": "^7.18.10", + "@babel/types": "^7.20.2", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -150,14 +150,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.20.0", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", "semver": "^6.3.0" }, "engines": { @@ -168,17 +168,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", - "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz", + "integrity": "sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-member-expression-to-functions": "^7.18.9", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-replace-supers": "^7.19.1", "@babel/helper-split-export-declaration": "^7.18.6" }, "engines": { @@ -189,9 +189,9 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", - "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", @@ -205,9 +205,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", - "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.17.7", @@ -243,13 +243,13 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -292,19 +292,19 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -323,9 +323,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -350,40 +350,40 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", - "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-member-expression-to-functions": "^7.18.9", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.20.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", - "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -402,18 +402,18 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -429,29 +429,29 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz", - "integrity": "sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", + "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.11", - "@babel/types": "^7.18.10" + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz", + "integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -472,9 +472,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz", + "integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -516,13 +516,13 @@ } }, "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", - "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz", + "integrity": "sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/helper-remap-async-to-generator": "^7.18.9", "@babel/plugin-syntax-async-generators": "^7.8.4" }, @@ -663,16 +663,16 @@ } }, "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", - "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz", + "integrity": "sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.18.8" + "@babel/plugin-transform-parameters": "^7.20.1" }, "engines": { "node": ">=6.9.0" @@ -828,12 +828,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", - "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1004,12 +1004,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", - "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz", + "integrity": "sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1019,17 +1019,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", - "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz", + "integrity": "sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.0", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.19.1", "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" }, @@ -1056,12 +1057,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz", - "integrity": "sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz", + "integrity": "sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1180,14 +1181,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", - "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1197,15 +1197,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", - "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" }, "engines": { "node": ">=6.9.0" @@ -1215,16 +1214,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", - "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", "dev": true, "dependencies": { "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-validator-identifier": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.19.1" }, "engines": { "node": ">=6.9.0" @@ -1250,13 +1248,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", - "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", + "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1297,12 +1295,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", - "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz", + "integrity": "sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1373,12 +1371,12 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", - "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" }, "engines": { @@ -1465,18 +1463,18 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", - "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-validator-option": "^7.18.6", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-class-static-block": "^7.18.6", "@babel/plugin-proposal-dynamic-import": "^7.18.6", @@ -1485,7 +1483,7 @@ "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", "@babel/plugin-proposal-optional-chaining": "^7.18.9", "@babel/plugin-proposal-private-methods": "^7.18.6", @@ -1496,7 +1494,7 @@ "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -1509,10 +1507,10 @@ "@babel/plugin-transform-arrow-functions": "^7.18.6", "@babel/plugin-transform-async-to-generator": "^7.18.6", "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.18.9", - "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", "@babel/plugin-transform-dotall-regex": "^7.18.6", "@babel/plugin-transform-duplicate-keys": "^7.18.9", "@babel/plugin-transform-exponentiation-operator": "^7.18.6", @@ -1520,30 +1518,30 @@ "@babel/plugin-transform-function-name": "^7.18.9", "@babel/plugin-transform-literals": "^7.18.9", "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.18.6", - "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", "@babel/plugin-transform-new-target": "^7.18.6", "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-parameters": "^7.20.1", "@babel/plugin-transform-property-literals": "^7.18.6", "@babel/plugin-transform-regenerator": "^7.18.6", "@babel/plugin-transform-reserved-words": "^7.18.6", "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-spread": "^7.19.0", "@babel/plugin-transform-sticky-regex": "^7.18.6", "@babel/plugin-transform-template-literals": "^7.18.9", "@babel/plugin-transform-typeof-symbol": "^7.18.9", "@babel/plugin-transform-unicode-escapes": "^7.18.10", "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.18.10", - "babel-plugin-polyfill-corejs2": "^0.3.2", - "babel-plugin-polyfill-corejs3": "^0.5.3", - "babel-plugin-polyfill-regenerator": "^0.4.0", - "core-js-compat": "^3.22.1", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", "semver": "^6.3.0" }, "engines": { @@ -1570,12 +1568,12 @@ } }, "node_modules/@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", + "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", "dev": true, "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.10" }, "engines": { "node": ">=6.9.0" @@ -1596,19 +1594,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", - "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", + "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", + "@babel/generator": "^7.20.1", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.11", - "@babel/types": "^7.18.10", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1617,13 +1615,13 @@ } }, "node_modules/@babel/types": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.2.tgz", + "integrity": "sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1631,9 +1629,9 @@ } }, "node_modules/@csstools/postcss-cascade-layers": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.0.5.tgz", - "integrity": "sha512-Id/9wBT7FkgFzdEpiEWrsVd4ltDxN0rI0QS0SChbeQiSuux3z21SJCRLu6h2cvCEUmaRi+VD0mHFj+GJD4GFnw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", "dev": true, "dependencies": { "@csstools/selector-specificity": "^2.0.2", @@ -1748,6 +1746,25 @@ "postcss": "^8.2" } }, + "node_modules/@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, "node_modules/@csstools/postcss-normalize-display-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", @@ -1821,6 +1838,25 @@ "postcss": "^8.2" } }, + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, "node_modules/@csstools/postcss-trigonometric-functions": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", @@ -1944,19 +1980,19 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, "node_modules/@types/eslint": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", - "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", "dev": true, "dependencies": { "@types/estree": "*", @@ -1986,9 +2022,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.7.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.1.tgz", - "integrity": "sha512-GKX1Qnqxo4S+Z/+Z8KKPLpH282LD7jLHWJcVryOflnsnH+BtSDfieR6ObwBMwpnNws0bUK8GI7z0unQf9bARNQ==", + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", "dev": true }, "node_modules/@types/parse-json": { @@ -2198,9 +2234,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2274,9 +2310,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.8", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.8.tgz", - "integrity": "sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==", + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", "dev": true, "funding": [ { @@ -2289,8 +2325,8 @@ } ], "dependencies": { - "browserslist": "^4.21.3", - "caniuse-lite": "^1.0.30001373", + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -2307,9 +2343,9 @@ } }, "node_modules/babel-loader": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", - "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", "dev": true, "dependencies": { "find-cache-dir": "^3.3.1", @@ -2325,23 +2361,14 @@ "webpack": ">=2" } }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "dependencies": { - "object.assign": "^4.1.0" - } - }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", - "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "dev": true, "dependencies": { "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.2", + "@babel/helper-define-polyfill-provider": "^0.3.3", "semver": "^6.1.1" }, "peerDependencies": { @@ -2349,25 +2376,25 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", - "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.2", - "core-js-compat": "^3.21.0" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz", - "integrity": "sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.2" + "@babel/helper-define-polyfill-provider": "^0.3.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" @@ -2392,22 +2419,28 @@ } }, "node_modules/bootstrap": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz", - "integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - }, + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", + "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], "peerDependencies": { "jquery": "1.9.1 - 3", "popper.js": "^1.16.1" } }, "node_modules/bootstrap-select": { - "version": "1.13.14", - "resolved": "https://registry.npmjs.org/bootstrap-select/-/bootstrap-select-1.13.14.tgz", - "integrity": "sha512-TiL2BobDTwPNV1pteDhukepamPXnbH33+B7D9oMI672KnsyDl0/j6lfQ+0TCL74TI1vaUv+gjL/0nvpmiTCzxQ==", + "version": "1.13.18", + "resolved": "https://registry.npmjs.org/bootstrap-select/-/bootstrap-select-1.13.18.tgz", + "integrity": "sha512-V1IzK4rxBq5FrJtkzSH6RmFLFBsjx50byFbfAf8jYyXROWs7ZpprGjdHeoyq2HSsHyjJhMMwjsQhRoYAfxCGow==", "peerDependencies": { "bootstrap": ">=3.0.0", "jquery": "1.9.1 - 3" @@ -2426,9 +2459,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, "funding": [ { @@ -2441,10 +2474,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" }, "bin": { "browserslist": "cli.js" @@ -2459,19 +2492,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2482,9 +2502,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001375", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz", - "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==", + "version": "1.0.30001431", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", + "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true, "funding": [ { @@ -2595,41 +2615,28 @@ "dev": true }, "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, "node_modules/core-js-compat": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", - "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.1.tgz", + "integrity": "sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A==", "dev": true, "dependencies": { - "browserslist": "^4.21.3", - "semver": "7.0.0" + "browserslist": "^4.21.4" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, "dependencies": { "@types/parse-json": "^4.0.0", @@ -2693,19 +2700,19 @@ } }, "node_modules/css-loader": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", - "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.2.tgz", + "integrity": "sha512-oqGbbVcBJkm8QwmnNzrFrWTnudnRZC+1eXikLJl0n4ljcfotgRifpg2a1lKy8jTrc4/d9A/ap1GFq1jDKG7J+Q==", "dev": true, "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.4.7", + "postcss": "^8.4.18", "postcss-modules-extract-imports": "^3.0.0", "postcss-modules-local-by-default": "^4.0.0", "postcss-modules-scope": "^3.0.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.3.5" + "semver": "^7.3.8" }, "engines": { "node": ">= 12.13.0" @@ -2719,9 +2726,9 @@ } }, "node_modules/css-loader/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -2749,9 +2756,9 @@ } }, "node_modules/cssdb": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-6.6.3.tgz", - "integrity": "sha512-7GDvDSmE+20+WcSMhP17Q1EVWUrLlbxxpMDqG731n8P99JhnQZHR9YvtjPvEHfjFUjvQJvdpKCjlKOX+xe4UVA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.1.0.tgz", + "integrity": "sha512-Sd99PrFgx28ez4GHu8yoQIufc/70h9oYowDf4EjeIKi8mac9whxRjhM3IaMr6EllP6KKKWtJrMfN6C7T9tIWvQ==", "dev": true, "funding": { "type": "opencollective", @@ -2804,26 +2811,10 @@ } } }, - "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/electron-to-chromium": { - "version": "1.4.215", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.215.tgz", - "integrity": "sha512-vqZxT8C5mlDZ//hQFhneHmOLnj1LhbzxV0+I1yqHV8SB1Oo4Y5Ne9+qQhwHl7O1s9s9cRuo2l5CoLEHdhMTwZg==", + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, "node_modules/emojis-list": { @@ -3059,20 +3050,6 @@ "node": ">=6.9.0" } }, - "node_modules/get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -3127,30 +3104,6 @@ "node": ">=4" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -3244,9 +3197,9 @@ } }, "node_modules/is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -3439,9 +3392,9 @@ } }, "node_modules/loader-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", - "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -3572,33 +3525,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.3.tgz", - "integrity": "sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/open-iconic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/open-iconic/-/open-iconic-1.1.1.tgz", @@ -3744,9 +3670,9 @@ } }, "node_modules/postcss": { - "version": "8.4.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", - "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "version": "8.4.19", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", + "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", "dev": true, "funding": [ { @@ -3878,9 +3804,9 @@ } }, "node_modules/postcss-custom-properties": { - "version": "12.1.8", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz", - "integrity": "sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==", + "version": "12.1.10", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.10.tgz", + "integrity": "sha512-U3BHdgrYhCrwTVcByFHs9EOBoqcKq4Lf3kXwbTi4hhq0qWhl/pDWq2THbv/ICX/Fl9KqeHBb8OVrTf2OaYF07A==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" @@ -3893,7 +3819,7 @@ "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.4" + "postcss": "^8.2" } }, "node_modules/postcss-custom-selectors": { @@ -4095,9 +4021,9 @@ } }, "node_modules/postcss-loader/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4193,9 +4119,9 @@ } }, "node_modules/postcss-nesting": { - "version": "10.1.10", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.10.tgz", - "integrity": "sha512-lqd7LXCq0gWc0wKXtoKDru5wEUNjm3OryLVNRZ8OnW8km6fSNUuFrjEhU3nklxXE2jvd4qrox566acgh+xQt8w==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", "dev": true, "dependencies": { "@csstools/selector-specificity": "^2.0.0", @@ -4279,57 +4205,59 @@ } }, "node_modules/postcss-preset-env": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.7.2.tgz", - "integrity": "sha512-1q0ih7EDsZmCb/FMDRvosna7Gsbdx8CvYO5hYT120hcp2ZAuOHpSzibujZ4JpIUcAC02PG6b+eftxqjTFh5BNA==", - "dev": true, - "dependencies": { - "@csstools/postcss-cascade-layers": "^1.0.4", - "@csstools/postcss-color-function": "^1.1.0", - "@csstools/postcss-font-format-keywords": "^1.0.0", - "@csstools/postcss-hwb-function": "^1.0.1", - "@csstools/postcss-ic-unit": "^1.0.0", - "@csstools/postcss-is-pseudo-class": "^2.0.6", - "@csstools/postcss-normalize-display-values": "^1.0.0", - "@csstools/postcss-oklab-function": "^1.1.0", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz", + "integrity": "sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==", + "dev": true, + "dependencies": { + "@csstools/postcss-cascade-layers": "^1.1.1", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", "@csstools/postcss-progressive-custom-properties": "^1.3.0", - "@csstools/postcss-stepped-value-functions": "^1.0.0", - "@csstools/postcss-trigonometric-functions": "^1.0.1", - "@csstools/postcss-unset-value": "^1.0.1", - "autoprefixer": "^10.4.7", - "browserslist": "^4.21.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", "css-blank-pseudo": "^3.0.3", "css-has-pseudo": "^3.0.4", "css-prefers-color-scheme": "^6.0.3", - "cssdb": "^6.6.3", - "postcss-attribute-case-insensitive": "^5.0.1", + "cssdb": "^7.1.0", + "postcss-attribute-case-insensitive": "^5.0.2", "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^4.2.3", + "postcss-color-functional-notation": "^4.2.4", "postcss-color-hex-alpha": "^8.0.4", - "postcss-color-rebeccapurple": "^7.1.0", + "postcss-color-rebeccapurple": "^7.1.1", "postcss-custom-media": "^8.0.2", - "postcss-custom-properties": "^12.1.8", + "postcss-custom-properties": "^12.1.10", "postcss-custom-selectors": "^6.0.3", - "postcss-dir-pseudo-class": "^6.0.4", - "postcss-double-position-gradients": "^3.1.1", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", "postcss-env-function": "^4.0.6", "postcss-focus-visible": "^6.0.4", "postcss-focus-within": "^5.0.4", "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.3", - "postcss-image-set-function": "^4.0.6", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.2.0", + "postcss-lab-function": "^4.2.1", "postcss-logical": "^5.0.4", "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.1.9", + "postcss-nesting": "^10.2.0", "postcss-opacity-percentage": "^1.1.2", - "postcss-overflow-shorthand": "^3.0.3", + "postcss-overflow-shorthand": "^3.0.4", "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.4", - "postcss-pseudo-class-any-link": "^7.1.5", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^6.0.0", + "postcss-selector-not": "^6.0.1", "postcss-value-parser": "^4.2.0" }, "engines": { @@ -4458,9 +4386,9 @@ "dev": true }, "node_modules/regenerate-unicode-properties": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", - "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "dev": true, "dependencies": { "regenerate": "^1.4.2" @@ -4470,47 +4398,47 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "dev": true }, "node_modules/regenerator-transform": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", - "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", "dev": true, "dependencies": { "@babel/runtime": "^7.8.4" } }, "node_modules/regexpu-core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", - "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", + "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", "dev": true, "dependencies": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" + "unicode-match-property-value-ecmascript": "^2.1.0" }, "engines": { "node": ">=4" } }, "node_modules/regjsgen": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", - "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==", "dev": true }, "node_modules/regjsparser": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", - "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, "dependencies": { "jsesc": "~0.5.0" @@ -4576,10 +4504,24 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -4588,9 +4530,9 @@ "dev": true }, "node_modules/sass": { - "version": "1.54.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz", - "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==", + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.56.1.tgz", + "integrity": "sha512-VpEyKpyBPCxE7qGDtOcdJ6fFbcpOM+Emu7uZLxVrkX8KVU/Dp5UF7WLvzqRuUhB6mqqQt1xffLoG+AndxTZrCQ==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -4605,9 +4547,9 @@ } }, "node_modules/sass-loader": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", - "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz", + "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==", "dev": true, "dependencies": { "klona": "^2.0.4", @@ -4622,7 +4564,7 @@ }, "peerDependencies": { "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "sass": "^1.3.0", "sass-embedded": "*", "webpack": "^5.0.0" @@ -4730,9 +4672,9 @@ } }, "node_modules/source-map-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz", - "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.1.tgz", + "integrity": "sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA==", "dev": true, "dependencies": { "abab": "^2.0.6", @@ -4810,9 +4752,9 @@ } }, "node_modules/terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", + "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.2", @@ -4828,16 +4770,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", - "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.7", + "@jridgewell/trace-mapping": "^0.3.14", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", - "terser": "^5.7.2" + "terser": "^5.14.1" }, "engines": { "node": ">= 10.13.0" @@ -4923,27 +4865,27 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, "funding": [ { @@ -4995,9 +4937,9 @@ } }, "node_modules/webpack": { - "version": "5.74.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", - "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "version": "5.75.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", + "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -5195,27 +5137,27 @@ } }, "@babel/compat-data": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz", + "integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==", "dev": true }, "@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz", + "integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", + "@babel/generator": "^7.20.2", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.1", + "@babel/parser": "^7.20.2", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -5224,12 +5166,12 @@ } }, "@babel/generator": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.4.tgz", + "integrity": "sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA==", "dev": true, "requires": { - "@babel/types": "^7.18.10", + "@babel/types": "^7.20.2", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -5267,36 +5209,36 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", "dev": true, "requires": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.20.0", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", "semver": "^6.3.0" } }, "@babel/helper-create-class-features-plugin": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", - "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz", + "integrity": "sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-member-expression-to-functions": "^7.18.9", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-replace-supers": "^7.19.1", "@babel/helper-split-export-declaration": "^7.18.6" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", - "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", @@ -5304,9 +5246,9 @@ } }, "@babel/helper-define-polyfill-provider": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", - "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "dev": true, "requires": { "@babel/helper-compilation-targets": "^7.17.7", @@ -5333,13 +5275,13 @@ } }, "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" } }, "@babel/helper-hoist-variables": { @@ -5370,19 +5312,19 @@ } }, "@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" } }, "@babel/helper-optimise-call-expression": { @@ -5395,9 +5337,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "dev": true }, "@babel/helper-remap-async-to-generator": { @@ -5413,34 +5355,34 @@ } }, "@babel/helper-replace-supers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", - "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-member-expression-to-functions": "^7.18.9", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" } }, "@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.20.2" } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", - "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "dev": true, "requires": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.0" } }, "@babel/helper-split-export-declaration": { @@ -5453,15 +5395,15 @@ } }, "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true }, "@babel/helper-validator-option": { @@ -5471,26 +5413,26 @@ "dev": true }, "@babel/helper-wrap-function": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz", - "integrity": "sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", + "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.11", - "@babel/types": "^7.18.10" + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" } }, "@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz", + "integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==", "dev": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.0" } }, "@babel/highlight": { @@ -5505,9 +5447,9 @@ } }, "@babel/parser": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz", + "integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -5531,13 +5473,13 @@ } }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", - "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz", + "integrity": "sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/helper-remap-async-to-generator": "^7.18.9", "@babel/plugin-syntax-async-generators": "^7.8.4" } @@ -5624,16 +5566,16 @@ } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", - "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz", + "integrity": "sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ==", "dev": true, "requires": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.18.8" + "@babel/plugin-transform-parameters": "^7.20.1" } }, "@babel/plugin-proposal-optional-catch-binding": { @@ -5735,12 +5677,12 @@ } }, "@babel/plugin-syntax-import-assertions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", - "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-syntax-json-strings": { @@ -5854,26 +5796,27 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", - "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz", + "integrity": "sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-classes": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", - "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz", + "integrity": "sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.0", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.19.1", "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" } @@ -5888,12 +5831,12 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz", - "integrity": "sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz", + "integrity": "sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-dotall-regex": { @@ -5964,39 +5907,36 @@ } }, "@babel/plugin-transform-modules-amd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", - "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", - "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", - "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-validator-identifier": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.19.1" } }, "@babel/plugin-transform-modules-umd": { @@ -6010,13 +5950,13 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", - "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", + "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-new-target": { @@ -6039,12 +5979,12 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", - "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz", + "integrity": "sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-property-literals": { @@ -6085,12 +6025,12 @@ } }, "@babel/plugin-transform-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", - "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" } }, @@ -6141,18 +6081,18 @@ } }, "@babel/preset-env": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", - "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", "dev": true, "requires": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-validator-option": "^7.18.6", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-class-static-block": "^7.18.6", "@babel/plugin-proposal-dynamic-import": "^7.18.6", @@ -6161,7 +6101,7 @@ "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", "@babel/plugin-proposal-optional-chaining": "^7.18.9", "@babel/plugin-proposal-private-methods": "^7.18.6", @@ -6172,7 +6112,7 @@ "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -6185,10 +6125,10 @@ "@babel/plugin-transform-arrow-functions": "^7.18.6", "@babel/plugin-transform-async-to-generator": "^7.18.6", "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.18.9", - "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", "@babel/plugin-transform-dotall-regex": "^7.18.6", "@babel/plugin-transform-duplicate-keys": "^7.18.9", "@babel/plugin-transform-exponentiation-operator": "^7.18.6", @@ -6196,30 +6136,30 @@ "@babel/plugin-transform-function-name": "^7.18.9", "@babel/plugin-transform-literals": "^7.18.9", "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.18.6", - "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", "@babel/plugin-transform-new-target": "^7.18.6", "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-parameters": "^7.20.1", "@babel/plugin-transform-property-literals": "^7.18.6", "@babel/plugin-transform-regenerator": "^7.18.6", "@babel/plugin-transform-reserved-words": "^7.18.6", "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-spread": "^7.19.0", "@babel/plugin-transform-sticky-regex": "^7.18.6", "@babel/plugin-transform-template-literals": "^7.18.9", "@babel/plugin-transform-typeof-symbol": "^7.18.9", "@babel/plugin-transform-unicode-escapes": "^7.18.10", "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.18.10", - "babel-plugin-polyfill-corejs2": "^0.3.2", - "babel-plugin-polyfill-corejs3": "^0.5.3", - "babel-plugin-polyfill-regenerator": "^0.4.0", - "core-js-compat": "^3.22.1", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", "semver": "^6.3.0" } }, @@ -6237,12 +6177,12 @@ } }, "@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", + "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", "dev": true, "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.10" } }, "@babel/template": { @@ -6257,38 +6197,38 @@ } }, "@babel/traverse": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", - "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", + "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", + "@babel/generator": "^7.20.1", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.11", - "@babel/types": "^7.18.10", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.2.tgz", + "integrity": "sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" } }, "@csstools/postcss-cascade-layers": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.0.5.tgz", - "integrity": "sha512-Id/9wBT7FkgFzdEpiEWrsVd4ltDxN0rI0QS0SChbeQiSuux3z21SJCRLu6h2cvCEUmaRi+VD0mHFj+GJD4GFnw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", "dev": true, "requires": { "@csstools/selector-specificity": "^2.0.2", @@ -6343,6 +6283,15 @@ "postcss-selector-parser": "^6.0.10" } }, + "@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, "@csstools/postcss-normalize-display-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", @@ -6380,6 +6329,15 @@ "postcss-value-parser": "^4.2.0" } }, + "@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, "@csstools/postcss-trigonometric-functions": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", @@ -6461,19 +6419,19 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, "@types/eslint": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", - "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", "dev": true, "requires": { "@types/estree": "*", @@ -6503,9 +6461,9 @@ "dev": true }, "@types/node": { - "version": "18.7.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.1.tgz", - "integrity": "sha512-GKX1Qnqxo4S+Z/+Z8KKPLpH282LD7jLHWJcVryOflnsnH+BtSDfieR6ObwBMwpnNws0bUK8GI7z0unQf9bARNQ==", + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", "dev": true }, "@types/parse-json": { @@ -6702,9 +6660,9 @@ "dev": true }, "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true }, "acorn-import-assertions": { @@ -6758,13 +6716,13 @@ } }, "autoprefixer": { - "version": "10.4.8", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.8.tgz", - "integrity": "sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==", + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", "dev": true, "requires": { - "browserslist": "^4.21.3", - "caniuse-lite": "^1.0.30001373", + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -6772,9 +6730,9 @@ } }, "babel-loader": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", - "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", "dev": true, "requires": { "find-cache-dir": "^3.3.1", @@ -6783,43 +6741,34 @@ "schema-utils": "^2.6.5" } }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, "babel-plugin-polyfill-corejs2": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", - "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "dev": true, "requires": { "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.2", + "@babel/helper-define-polyfill-provider": "^0.3.3", "semver": "^6.1.1" } }, "babel-plugin-polyfill-corejs3": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", - "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.2", - "core-js-compat": "^3.21.0" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" } }, "babel-plugin-polyfill-regenerator": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz", - "integrity": "sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.2" + "@babel/helper-define-polyfill-provider": "^0.3.3" } }, "big.js": { @@ -6835,15 +6784,15 @@ "dev": true }, "bootstrap": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz", - "integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", + "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==", "requires": {} }, "bootstrap-select": { - "version": "1.13.14", - "resolved": "https://registry.npmjs.org/bootstrap-select/-/bootstrap-select-1.13.14.tgz", - "integrity": "sha512-TiL2BobDTwPNV1pteDhukepamPXnbH33+B7D9oMI672KnsyDl0/j6lfQ+0TCL74TI1vaUv+gjL/0nvpmiTCzxQ==", + "version": "1.13.18", + "resolved": "https://registry.npmjs.org/bootstrap-select/-/bootstrap-select-1.13.18.tgz", + "integrity": "sha512-V1IzK4rxBq5FrJtkzSH6RmFLFBsjx50byFbfAf8jYyXROWs7ZpprGjdHeoyq2HSsHyjJhMMwjsQhRoYAfxCGow==", "requires": {} }, "braces": { @@ -6856,15 +6805,15 @@ } }, "browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" } }, "buffer-from": { @@ -6873,16 +6822,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -6890,9 +6829,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001375", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz", - "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==", + "version": "1.0.30001431", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", + "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true }, "chalk": { @@ -6973,36 +6912,24 @@ "dev": true }, "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, "core-js-compat": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", - "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.1.tgz", + "integrity": "sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A==", "dev": true, "requires": { - "browserslist": "^4.21.3", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } + "browserslist": "^4.21.4" } }, "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, "requires": { "@types/parse-json": "^4.0.0", @@ -7042,25 +6969,25 @@ } }, "css-loader": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", - "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.2.tgz", + "integrity": "sha512-oqGbbVcBJkm8QwmnNzrFrWTnudnRZC+1eXikLJl0n4ljcfotgRifpg2a1lKy8jTrc4/d9A/ap1GFq1jDKG7J+Q==", "dev": true, "requires": { "icss-utils": "^5.1.0", - "postcss": "^8.4.7", + "postcss": "^8.4.18", "postcss-modules-extract-imports": "^3.0.0", "postcss-modules-local-by-default": "^4.0.0", "postcss-modules-scope": "^3.0.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.3.5" + "semver": "^7.3.8" }, "dependencies": { "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -7076,9 +7003,9 @@ "requires": {} }, "cssdb": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-6.6.3.tgz", - "integrity": "sha512-7GDvDSmE+20+WcSMhP17Q1EVWUrLlbxxpMDqG731n8P99JhnQZHR9YvtjPvEHfjFUjvQJvdpKCjlKOX+xe4UVA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.1.0.tgz", + "integrity": "sha512-Sd99PrFgx28ez4GHu8yoQIufc/70h9oYowDf4EjeIKi8mac9whxRjhM3IaMr6EllP6KKKWtJrMfN6C7T9tIWvQ==", "dev": true }, "cssesc": { @@ -7113,20 +7040,10 @@ "ms": "2.1.2" } }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, "electron-to-chromium": { - "version": "1.4.215", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.215.tgz", - "integrity": "sha512-vqZxT8C5mlDZ//hQFhneHmOLnj1LhbzxV0+I1yqHV8SB1Oo4Y5Ne9+qQhwHl7O1s9s9cRuo2l5CoLEHdhMTwZg==", + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, "emojis-list": { @@ -7296,17 +7213,6 @@ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, - "get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -7349,21 +7255,6 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, "iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -7428,9 +7319,9 @@ } }, "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, "requires": { "has": "^1.0.3" @@ -7574,9 +7465,9 @@ "dev": true }, "loader-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", - "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -7674,24 +7565,6 @@ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.3.tgz", - "integrity": "sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, "open-iconic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/open-iconic/-/open-iconic-1.1.1.tgz", @@ -7793,9 +7666,9 @@ "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" }, "postcss": { - "version": "8.4.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", - "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "version": "8.4.19", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", + "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", "dev": true, "requires": { "nanoid": "^3.3.4", @@ -7858,9 +7731,9 @@ } }, "postcss-custom-properties": { - "version": "12.1.8", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz", - "integrity": "sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==", + "version": "12.1.10", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.10.tgz", + "integrity": "sha512-U3BHdgrYhCrwTVcByFHs9EOBoqcKq4Lf3kXwbTi4hhq0qWhl/pDWq2THbv/ICX/Fl9KqeHBb8OVrTf2OaYF07A==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -7973,9 +7846,9 @@ }, "dependencies": { "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -8034,9 +7907,9 @@ } }, "postcss-nesting": { - "version": "10.1.10", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.10.tgz", - "integrity": "sha512-lqd7LXCq0gWc0wKXtoKDru5wEUNjm3OryLVNRZ8OnW8km6fSNUuFrjEhU3nklxXE2jvd4qrox566acgh+xQt8w==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", "dev": true, "requires": { "@csstools/selector-specificity": "^2.0.0", @@ -8075,57 +7948,59 @@ } }, "postcss-preset-env": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.7.2.tgz", - "integrity": "sha512-1q0ih7EDsZmCb/FMDRvosna7Gsbdx8CvYO5hYT120hcp2ZAuOHpSzibujZ4JpIUcAC02PG6b+eftxqjTFh5BNA==", - "dev": true, - "requires": { - "@csstools/postcss-cascade-layers": "^1.0.4", - "@csstools/postcss-color-function": "^1.1.0", - "@csstools/postcss-font-format-keywords": "^1.0.0", - "@csstools/postcss-hwb-function": "^1.0.1", - "@csstools/postcss-ic-unit": "^1.0.0", - "@csstools/postcss-is-pseudo-class": "^2.0.6", - "@csstools/postcss-normalize-display-values": "^1.0.0", - "@csstools/postcss-oklab-function": "^1.1.0", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz", + "integrity": "sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==", + "dev": true, + "requires": { + "@csstools/postcss-cascade-layers": "^1.1.1", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", "@csstools/postcss-progressive-custom-properties": "^1.3.0", - "@csstools/postcss-stepped-value-functions": "^1.0.0", - "@csstools/postcss-trigonometric-functions": "^1.0.1", - "@csstools/postcss-unset-value": "^1.0.1", - "autoprefixer": "^10.4.7", - "browserslist": "^4.21.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", "css-blank-pseudo": "^3.0.3", "css-has-pseudo": "^3.0.4", "css-prefers-color-scheme": "^6.0.3", - "cssdb": "^6.6.3", - "postcss-attribute-case-insensitive": "^5.0.1", + "cssdb": "^7.1.0", + "postcss-attribute-case-insensitive": "^5.0.2", "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^4.2.3", + "postcss-color-functional-notation": "^4.2.4", "postcss-color-hex-alpha": "^8.0.4", - "postcss-color-rebeccapurple": "^7.1.0", + "postcss-color-rebeccapurple": "^7.1.1", "postcss-custom-media": "^8.0.2", - "postcss-custom-properties": "^12.1.8", + "postcss-custom-properties": "^12.1.10", "postcss-custom-selectors": "^6.0.3", - "postcss-dir-pseudo-class": "^6.0.4", - "postcss-double-position-gradients": "^3.1.1", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", "postcss-env-function": "^4.0.6", "postcss-focus-visible": "^6.0.4", "postcss-focus-within": "^5.0.4", "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.3", - "postcss-image-set-function": "^4.0.6", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.2.0", + "postcss-lab-function": "^4.2.1", "postcss-logical": "^5.0.4", "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.1.9", + "postcss-nesting": "^10.2.0", "postcss-opacity-percentage": "^1.1.2", - "postcss-overflow-shorthand": "^3.0.3", + "postcss-overflow-shorthand": "^3.0.4", "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.4", - "postcss-pseudo-class-any-link": "^7.1.5", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^6.0.0", + "postcss-selector-not": "^6.0.1", "postcss-value-parser": "^4.2.0" } }, @@ -8210,53 +8085,53 @@ "dev": true }, "regenerate-unicode-properties": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", - "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "dev": true, "requires": { "regenerate": "^1.4.2" } }, "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "dev": true }, "regenerator-transform": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", - "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", "dev": true, "requires": { "@babel/runtime": "^7.8.4" } }, "regexpu-core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", - "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", + "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", "dev": true, "requires": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" + "unicode-match-property-value-ecmascript": "^2.1.0" } }, "regjsgen": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", - "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==", "dev": true }, "regjsparser": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", - "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -8305,9 +8180,9 @@ "dev": true }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "safer-buffer": { @@ -8317,9 +8192,9 @@ "dev": true }, "sass": { - "version": "1.54.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz", - "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==", + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.56.1.tgz", + "integrity": "sha512-VpEyKpyBPCxE7qGDtOcdJ6fFbcpOM+Emu7uZLxVrkX8KVU/Dp5UF7WLvzqRuUhB6mqqQt1xffLoG+AndxTZrCQ==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", @@ -8328,9 +8203,9 @@ } }, "sass-loader": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", - "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz", + "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==", "dev": true, "requires": { "klona": "^2.0.4", @@ -8400,9 +8275,9 @@ "dev": true }, "source-map-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz", - "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.1.tgz", + "integrity": "sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA==", "dev": true, "requires": { "abab": "^2.0.6", @@ -8449,9 +8324,9 @@ "dev": true }, "terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", + "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==", "dev": true, "requires": { "@jridgewell/source-map": "^0.3.2", @@ -8461,16 +8336,16 @@ } }, "terser-webpack-plugin": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", - "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.7", + "@jridgewell/trace-mapping": "^0.3.14", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", - "terser": "^5.7.2" + "terser": "^5.14.1" }, "dependencies": { "schema-utils": { @@ -8518,21 +8393,21 @@ } }, "unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "dev": true }, "unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true }, "update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, "requires": { "escalade": "^3.1.1", @@ -8565,9 +8440,9 @@ } }, "webpack": { - "version": "5.74.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", - "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "version": "5.75.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", + "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", diff --git a/src/main/resources/package.json b/src/main/resources/package.json index 9410a851..086cf652 100644 --- a/src/main/resources/package.json +++ b/src/main/resources/package.json @@ -7,28 +7,28 @@ }, "dependencies": { "activity-detector": "^3.0.0", - "bootstrap": "4.6.1", - "bootstrap-select": "1.13.14", - "datatables.net": "1.12.1", - "datatables.net-bs4": "1.12.1", - "jquery": "3.5.1", - "jquery-ui": "1.13.2", - "open-iconic": "1.1.1", - "popper.js": "1.16.1" + "bootstrap": "~4.6.2", + "bootstrap-select": "~1.13.18", + "datatables.net": "~1.12.1", + "datatables.net-bs4": "~1.12.1", + "jquery": "~3.5.1", + "jquery-ui": "~1.13.2", + "open-iconic": "~1.1.1", + "popper.js": "~1.16.1" }, "devDependencies": { - "@babel/core": "^7.18.10", - "@babel/preset-env": "^7.18.10", - "babel-loader": "^8.2.5", - "css-loader": "^6.7.1", - "postcss": "^8.4.16", + "@babel/core": "^7.20.2", + "@babel/preset-env": "^7.20.2", + "babel-loader": "^8.3.0", + "css-loader": "^6.7.2", + "postcss": "^8.4.19", "postcss-loader": "^7.0.1", - "postcss-preset-env": "^7.7.2", - "sass": "^1.54.4", - "sass-loader": "^13.0.2", - "source-map-loader": "^4.0.0", + "postcss-preset-env": "^7.8.3", + "sass": "^1.56.1", + "sass-loader": "^13.2.0", + "source-map-loader": "^4.0.1", "style-loader": "^3.3.1", - "webpack": "^5.74.0", + "webpack": "^5.75.0", "webpack-cli": "^4.10.0" }, "browserslist": [ From 0f1b8d1335e91abc262d5f7099129533309fdaa7 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Tue, 22 Nov 2022 10:32:30 -0800 Subject: [PATCH 26/34] Add explicit default null for timestamp columns --- .../java/ubc/pavlab/rdp/MigrationConfig.java | 2 +- ...1.5.0.4__add_various_timestamp_columns.sql | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/ubc/pavlab/rdp/MigrationConfig.java b/src/main/java/ubc/pavlab/rdp/MigrationConfig.java index 9cdeb299..df665e8b 100644 --- a/src/main/java/ubc/pavlab/rdp/MigrationConfig.java +++ b/src/main/java/ubc/pavlab/rdp/MigrationConfig.java @@ -57,7 +57,7 @@ private static class ExpectedMigrationInfo { new ExpectedMigrationInfo( "1.4.6", 399726006, 504755998, NewChecksumReason.FLYWAY_3 ), new ExpectedMigrationInfo( "1.4.11", 1536441374, 709491229, NewChecksumReason.FLYWAY_3 ), new ExpectedMigrationInfo( "1.4.11.1", -1312864724, -907949910, NewChecksumReason.FLYWAY_3 ), - new ExpectedMigrationInfo( "1.5.0.4", 625408518, 1560455114, NewChecksumReason.MYSQL_5_7_REGRESSION ) + new ExpectedMigrationInfo( "1.5.0.4", 625408518, -1065145387, NewChecksumReason.MYSQL_5_7_REGRESSION ) }; /** diff --git a/src/main/resources/db/migration/common/V1.5.0.4__add_various_timestamp_columns.sql b/src/main/resources/db/migration/common/V1.5.0.4__add_various_timestamp_columns.sql index 8a7ff6a9..14dd5dde 100644 --- a/src/main/resources/db/migration/common/V1.5.0.4__add_various_timestamp_columns.sql +++ b/src/main/resources/db/migration/common/V1.5.0.4__add_various_timestamp_columns.sql @@ -1,22 +1,22 @@ alter table user - add column created_at timestamp null; + add column created_at timestamp null default null; alter table user - add column modified_at timestamp null; + add column modified_at timestamp null default null; alter table user - add column enabled_at timestamp null after enabled; + add column enabled_at timestamp null default null after enabled; alter table user - add column contact_email_verified_at timestamp null after contact_email_verified; + add column contact_email_verified_at timestamp null default null after contact_email_verified; alter table gene - add column created_at timestamp null; + add column created_at timestamp null default null; alter table user_ontology_term - add column created_at timestamp null; + add column created_at timestamp null default null; alter table user_organ - add column created_at timestamp null; + add column created_at timestamp null default null; alter table term - add column created_at timestamp null; + add column created_at timestamp null default null; alter table access_token - add column created_at timestamp null; + add column created_at timestamp null default null; alter table verification_token - add column created_at timestamp null; + add column created_at timestamp null default null; alter table password_reset_token - add column created_at timestamp null; \ No newline at end of file + add column created_at timestamp null default null; \ No newline at end of file From b852d309156b2c73d93c0e938e9c2a8a8657bb6f Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Tue, 22 Nov 2022 10:33:29 -0800 Subject: [PATCH 27/34] Add a migration to mitigate possible zero-filled timestamps --- .../V1.5.0.6__fix_zero_filled_timestamps.sql | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/main/resources/db/migration/common/V1.5.0.6__fix_zero_filled_timestamps.sql diff --git a/src/main/resources/db/migration/common/V1.5.0.6__fix_zero_filled_timestamps.sql b/src/main/resources/db/migration/common/V1.5.0.6__fix_zero_filled_timestamps.sql new file mode 100644 index 00000000..65b3f75a --- /dev/null +++ b/src/main/resources/db/migration/common/V1.5.0.6__fix_zero_filled_timestamps.sql @@ -0,0 +1,48 @@ +-- There's a bug in the 1.5.0.4 migration from the releases prior to 1.5.4 that resulted in timestamp columns being +-- initialized with either CURRENT_TIMESTAMP() or zeroes on MySQL. For MySQL 5.7, since only one column can default to +-- CURRENT_TIMESTAMP(), the migration is broken. This is fixed in the 1.5.4 release by explicitly defaulting to NULL. +-- However, if a user ran the that buggy migration, the database will contain zeroed timestamps. + +alter table user + modify column created_at timestamp null default null; +alter table user + modify column modified_at timestamp null default null; +alter table user + modify column enabled_at timestamp null default null; +alter table user + modify column contact_email_verified_at timestamp null default null; +alter table gene + modify column created_at timestamp null default null; +alter table user_ontology_term + modify column created_at timestamp null default null; +alter table user_organ + modify column created_at timestamp null default null; +alter table term + modify column created_at timestamp null default null; +alter table access_token + modify column created_at timestamp null default null; +alter table verification_token + modify column created_at timestamp null default null; +alter table password_reset_token + modify column created_at timestamp null default null; + +-- unfortunately, we cannot fix the other columns +update user +set created_at = null, + modified_at = null, + enabled_at = null, + contact_email_verified_at = null; +update gene +set created_at = null; +update user_ontology_term +set created_at = null; +update user_organ +set created_at = null; +update term +set created_at = null; +update access_token +set created_at = null; +update verification_token +set created_at = null; +update password_reset_token +set created_at = null; \ No newline at end of file From ca920ec50de8525253a2d675e27e05e6e055caa2 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Tue, 22 Nov 2022 10:33:51 -0800 Subject: [PATCH 28/34] Add testing support for MySQL 5.6 Downgrade Flyway to 5.2.4 --- docker-compose.yml | 9 +++++++++ docs/installation.md | 12 +++++++++--- pom.xml | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 2db9c38a..f79b52e8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,12 @@ +mysql56: + image: mysql:5.6 + ports: + - "3306:3306" + environment: + - MYSQL_USER=springuser + - MYSQL_PASSWORD=ThePassword + - MYSQL_DATABASE=db_example + - MYSQL_RANDOM_ROOT_PASSWORD=true mysql57: image: mysql:5.7 ports: diff --git a/docs/installation.md b/docs/installation.md index 8f3bacc5..d9bca79d 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -29,9 +29,15 @@ create user ''@'%' identified by ''; grant all on rdp.* to ''@'%'; ``` -If you're using MySQL 5.6 or prior, use the `utf8` character set. It is a 3 bytes subset of the typical 4-bytes UTF-8 -character encoding. Otherwise, you will face issues with the index size limit of 767 bytes due to some of our indexed -columns containing 255 characters (4 * 255 = 1020 > 767, but 3 * 255 = 765). +If you're using MySQL 5.6 or prior, use the `utf8mb3` character set. It is a 3 bytes subset of the typical 4-bytes +UTF-8 character encoding. Otherwise, you will face issues with the index size limit of 767 bytes due to some of our +indexed columns containing 255 characters (4 * 255 = 1020 > 767, but 3 * 255 = 765). + +You should also adjust Hibernate dialect by adding the following to your `application.properties`: + +```properties +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL55Dialect +``` ## Setup application.properties diff --git a/pom.xml b/pom.xml index 56cc2df4..cff18b30 100644 --- a/pom.xml +++ b/pom.xml @@ -198,7 +198,7 @@ 1.8 - 7.15.0 + 5.2.4 false From 0671f7b0dbfeaadb1576c25ba8dc457a7f5c2a7f Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Tue, 22 Nov 2022 11:02:02 -0800 Subject: [PATCH 29/34] Replace deprecated MySQL57InnoDBDialect with storage_engine property --- src/main/resources/application.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a10a095d..4a99c2bf 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -19,7 +19,8 @@ spring.servlet.multipart.max-request-size=50MB # = JPA / HIBERNATE # =============================== spring.jpa.hibernate.ddl-auto=validate -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57InnoDBDialect +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect +spring.jpa.properties.hibernate.dialect.storage_engine=innodb spring.jpa.properties.hibernate.jdbc.batch_size=10 spring.jpa.properties.hibernate.id.new_generator_mappings=false spring.jpa.properties.hibernate.cache.use_second_level_cache=true From 1da9a05a9f7ac6542554d320b7fc7a0e6b2c804a Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Tue, 22 Nov 2022 11:03:36 -0800 Subject: [PATCH 30/34] Add MariaDB106Dialect to installation docs --- docs/installation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/installation.md b/docs/installation.md index d9bca79d..335136cb 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -39,6 +39,12 @@ You should also adjust Hibernate dialect by adding the following to your `applic spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL55Dialect ``` +Likewise, if you are using MariaDB, for which we recommend the current 10.6 LTS, use the following dialect: + +```properties +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDB106Dialect +``` + ## Setup application.properties In the working directory of the Web application, create an `application.properties` From 514d26ec4efbf832ce77ad232c1e080f4c6343c6 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Tue, 22 Nov 2022 11:27:45 -0800 Subject: [PATCH 31/34] Use a timestamp for access_token.expiry_date --- .../common/V1.5.4__change_access_token_expiry_to_timestamp.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/main/resources/db/migration/common/V1.5.4__change_access_token_expiry_to_timestamp.sql diff --git a/src/main/resources/db/migration/common/V1.5.4__change_access_token_expiry_to_timestamp.sql b/src/main/resources/db/migration/common/V1.5.4__change_access_token_expiry_to_timestamp.sql new file mode 100644 index 00000000..b533cc66 --- /dev/null +++ b/src/main/resources/db/migration/common/V1.5.4__change_access_token_expiry_to_timestamp.sql @@ -0,0 +1,2 @@ +alter table access_token + modify column expiry_date timestamp not null; \ No newline at end of file From f7eb66550bf71c89493d08dc87789b8f8b1522ba Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Tue, 22 Nov 2022 11:49:59 -0800 Subject: [PATCH 32/34] Get rid of java.sql.Timestamp and java.util.Date --- .../pavlab/rdp/controllers/AdminController.java | 3 +-- .../java/ubc/pavlab/rdp/model/AccessToken.java | 6 ++---- src/main/java/ubc/pavlab/rdp/model/Gene.java | 5 ++--- .../ubc/pavlab/rdp/model/PasswordResetToken.java | 4 ++-- src/main/java/ubc/pavlab/rdp/model/Profile.java | 5 ++--- src/main/java/ubc/pavlab/rdp/model/Token.java | 10 ++++++---- src/main/java/ubc/pavlab/rdp/model/User.java | 10 +++++----- .../java/ubc/pavlab/rdp/model/UserContent.java | 4 ++-- src/main/java/ubc/pavlab/rdp/model/UserGene.java | 4 ++-- .../java/ubc/pavlab/rdp/model/UserOrgan.java | 4 ++-- src/main/java/ubc/pavlab/rdp/model/UserTerm.java | 4 ++-- .../ubc/pavlab/rdp/model/VerificationToken.java | 4 ++-- .../rdp/model/ontology/UserOntologyTerm.java | 4 ++-- .../PasswordResetTokenRepository.java | 8 +++----- .../VerificationTokenRepository.java | 6 +++--- .../pavlab/rdp/services/EmailServiceImpl.java | 2 +- .../ubc/pavlab/rdp/services/UserServiceImpl.java | 11 +++++------ .../java/ubc/pavlab/rdp/util/GeneInfoParser.java | 16 ++++++++-------- src/main/resources/templates/admin/user.html | 4 ++-- src/main/resources/templates/search/user.html | 2 +- .../PasswordResetTokenRepositoryTest.java | 13 +++++++------ .../rdp/repositories/UserGeneRepositoryTest.java | 15 +++++++++------ .../repositories/UserOrganRepositoryTest.java | 3 ++- .../rdp/repositories/UserRepositoryTest.java | 4 +++- .../VerificationTokenRepositoryTest.java | 13 ++++++++----- .../ontology/UserOntologyRepositoryTest.java | 5 ++++- .../rdp/security/PrivacyServiceImplTest.java | 5 ++--- .../rdp/services/EmailServiceImplTest.java | 7 +++---- .../rdp/services/GeneInfoServiceImplTest.java | 12 ++++++------ .../pavlab/rdp/services/UserServiceImplTest.java | 15 +++++++-------- src/test/java/ubc/pavlab/rdp/util/TestUtils.java | 3 +-- 31 files changed, 107 insertions(+), 104 deletions(-) diff --git a/src/main/java/ubc/pavlab/rdp/controllers/AdminController.java b/src/main/java/ubc/pavlab/rdp/controllers/AdminController.java index 9b7a040c..908e81da 100644 --- a/src/main/java/ubc/pavlab/rdp/controllers/AdminController.java +++ b/src/main/java/ubc/pavlab/rdp/controllers/AdminController.java @@ -53,7 +53,6 @@ import javax.validation.constraints.Size; import java.io.*; import java.net.URL; -import java.sql.Timestamp; import java.text.MessageFormat; import java.time.Duration; import java.time.Instant; @@ -134,7 +133,7 @@ public Object createServiceAccount( @Validated(User.ValidationServiceAccount.cla user.setEmail( serviceEmail ); user.setEnabled( true ); - user.setEnabledAt( Timestamp.from( Instant.now() ) ); + user.setEnabledAt( Instant.now() ); Profile profile = user.getProfile(); profile.setPrivacyLevel( PrivacyLevelType.PRIVATE ); diff --git a/src/main/java/ubc/pavlab/rdp/model/AccessToken.java b/src/main/java/ubc/pavlab/rdp/model/AccessToken.java index 76f6c8ae..875fafd6 100644 --- a/src/main/java/ubc/pavlab/rdp/model/AccessToken.java +++ b/src/main/java/ubc/pavlab/rdp/model/AccessToken.java @@ -7,10 +7,8 @@ import ubc.pavlab.rdp.model.enums.PrivacyLevelType; import javax.persistence.*; -import java.sql.Timestamp; import java.time.Duration; -import java.time.Period; -import java.time.temporal.ChronoUnit; +import java.time.Instant; import java.time.temporal.TemporalAmount; import java.util.Optional; @@ -31,7 +29,7 @@ public class AccessToken extends Token implements UserContent { private User user; @CreatedDate - private Timestamp createdAt; + private Instant createdAt; @Override protected TemporalAmount getDuration() { diff --git a/src/main/java/ubc/pavlab/rdp/model/Gene.java b/src/main/java/ubc/pavlab/rdp/model/Gene.java index 942ac82f..cad98756 100644 --- a/src/main/java/ubc/pavlab/rdp/model/Gene.java +++ b/src/main/java/ubc/pavlab/rdp/model/Gene.java @@ -9,7 +9,7 @@ import javax.persistence.*; import java.io.Serializable; -import java.util.Date; +import java.time.LocalDate; /** * Created by mjacobson on 17/01/18. @@ -41,7 +41,6 @@ public abstract class Gene implements Serializable { private String aliases; @JsonIgnore - @Temporal(TemporalType.DATE) @Column(name = "modification_date") - private Date modificationDate; + private LocalDate modificationDate; } diff --git a/src/main/java/ubc/pavlab/rdp/model/PasswordResetToken.java b/src/main/java/ubc/pavlab/rdp/model/PasswordResetToken.java index 3d0490b7..ed8f60ec 100644 --- a/src/main/java/ubc/pavlab/rdp/model/PasswordResetToken.java +++ b/src/main/java/ubc/pavlab/rdp/model/PasswordResetToken.java @@ -9,8 +9,8 @@ import ubc.pavlab.rdp.model.enums.PrivacyLevelType; import javax.persistence.*; -import java.sql.Timestamp; import java.time.Duration; +import java.time.Instant; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAmount; import java.util.Optional; @@ -36,7 +36,7 @@ public class PasswordResetToken extends Token implements UserContent { private User user; @CreatedDate - private Timestamp createdAt; + private Instant createdAt; @Override public Optional getOwner() { diff --git a/src/main/java/ubc/pavlab/rdp/model/Profile.java b/src/main/java/ubc/pavlab/rdp/model/Profile.java index f8e48b3a..cafcc688 100644 --- a/src/main/java/ubc/pavlab/rdp/model/Profile.java +++ b/src/main/java/ubc/pavlab/rdp/model/Profile.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.media.SchemaProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -17,8 +16,8 @@ import javax.persistence.*; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; -import java.sql.Timestamp; import java.text.MessageFormat; +import java.time.Instant; import java.util.HashSet; import java.util.Set; @@ -78,7 +77,7 @@ public String getFullName() { @JsonIgnore @Column(name = "contact_email_verified_at") - private Timestamp contactEmailVerifiedAt; + private Instant contactEmailVerifiedAt; @Column(name = "website") @URL diff --git a/src/main/java/ubc/pavlab/rdp/model/Token.java b/src/main/java/ubc/pavlab/rdp/model/Token.java index a5c0c60b..bd13a54d 100644 --- a/src/main/java/ubc/pavlab/rdp/model/Token.java +++ b/src/main/java/ubc/pavlab/rdp/model/Token.java @@ -1,12 +1,14 @@ package ubc.pavlab.rdp.model; -import lombok.*; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; import org.hibernate.annotations.NaturalId; import javax.persistence.Column; import javax.persistence.MappedSuperclass; import java.io.Serializable; -import java.sql.Timestamp; import java.time.Instant; import java.time.temporal.TemporalAmount; @@ -22,7 +24,7 @@ public abstract class Token implements Serializable { private String token; @Column(name = "expiry_date", nullable = false) - private Timestamp expiryDate; + private Instant expiryDate; protected abstract TemporalAmount getDuration(); @@ -32,6 +34,6 @@ private Instant calculateExpiryDate() { public void updateToken( final String token ) { this.token = token; - this.expiryDate = Timestamp.from( calculateExpiryDate() ); + this.expiryDate = calculateExpiryDate(); } } diff --git a/src/main/java/ubc/pavlab/rdp/model/User.java b/src/main/java/ubc/pavlab/rdp/model/User.java index 52c9a535..eb1fa50b 100644 --- a/src/main/java/ubc/pavlab/rdp/model/User.java +++ b/src/main/java/ubc/pavlab/rdp/model/User.java @@ -28,8 +28,8 @@ import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URI; -import java.sql.Timestamp; import java.text.MessageFormat; +import java.time.Instant; import java.util.*; import java.util.stream.Collectors; @@ -106,14 +106,14 @@ public static Comparator getComparator() { */ @CreatedDate @JsonIgnore - private Timestamp createdAt; + private Instant createdAt; /** * Last moment when this user profile was updated, or null if unknown. */ @LastModifiedDate @JsonIgnore - private Timestamp modifiedAt; + private Instant modifiedAt; @JsonIgnore @Column(name = "enabled", nullable = false) @@ -124,7 +124,7 @@ public static Comparator getComparator() { */ @Column(name = "enabled_at") @JsonIgnore - private Timestamp enabledAt; + private Instant enabledAt; @ManyToMany @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) @@ -259,7 +259,7 @@ public String getVerifiedContactEmailJsonValue() { @SuppressWarnings("unused") @JsonIgnore @Transient - public Optional getVerifiedAtContactEmail() { + public Optional getVerifiedAtContactEmail() { if ( profile.isContactEmailVerified() ) { return Optional.ofNullable( profile.getContactEmailVerifiedAt() ); } else if ( enabled ) { diff --git a/src/main/java/ubc/pavlab/rdp/model/UserContent.java b/src/main/java/ubc/pavlab/rdp/model/UserContent.java index 98274f5e..bfca3e0a 100644 --- a/src/main/java/ubc/pavlab/rdp/model/UserContent.java +++ b/src/main/java/ubc/pavlab/rdp/model/UserContent.java @@ -3,7 +3,7 @@ import lombok.NonNull; import ubc.pavlab.rdp.model.enums.PrivacyLevelType; -import java.sql.Timestamp; +import java.time.Instant; import java.util.Optional; /** @@ -28,5 +28,5 @@ public interface UserContent { /** * Exact moment when this user-associated content was created, or null if unknown. */ - Timestamp getCreatedAt(); + Instant getCreatedAt(); } diff --git a/src/main/java/ubc/pavlab/rdp/model/UserGene.java b/src/main/java/ubc/pavlab/rdp/model/UserGene.java index daddc8a4..3b8e40dc 100644 --- a/src/main/java/ubc/pavlab/rdp/model/UserGene.java +++ b/src/main/java/ubc/pavlab/rdp/model/UserGene.java @@ -15,8 +15,8 @@ import ubc.pavlab.rdp.model.enums.TierType; import javax.persistence.*; -import java.sql.Timestamp; import java.text.MessageFormat; +import java.time.Instant; import java.util.Comparator; import java.util.Optional; import java.util.UUID; @@ -102,7 +102,7 @@ public static Comparator getComparator() { */ @CreatedDate @JsonIgnore - private Timestamp createdAt; + private Instant createdAt; @Nullable @ManyToOne diff --git a/src/main/java/ubc/pavlab/rdp/model/UserOrgan.java b/src/main/java/ubc/pavlab/rdp/model/UserOrgan.java index 997b51df..621652cd 100644 --- a/src/main/java/ubc/pavlab/rdp/model/UserOrgan.java +++ b/src/main/java/ubc/pavlab/rdp/model/UserOrgan.java @@ -10,7 +10,7 @@ import ubc.pavlab.rdp.model.enums.PrivacyLevelType; import javax.persistence.*; -import java.sql.Timestamp; +import java.time.Instant; import java.util.Optional; /** @@ -42,7 +42,7 @@ public class UserOrgan extends Organ implements UserContent { @CreatedDate @JsonIgnore - private Timestamp createdAt; + private Instant createdAt; public static UserOrgan createFromOrganInfo( User user, OrganInfo organInfo ) { UserOrgan userOrgan = new UserOrgan(); diff --git a/src/main/java/ubc/pavlab/rdp/model/UserTerm.java b/src/main/java/ubc/pavlab/rdp/model/UserTerm.java index ec1da9c1..03629c4b 100644 --- a/src/main/java/ubc/pavlab/rdp/model/UserTerm.java +++ b/src/main/java/ubc/pavlab/rdp/model/UserTerm.java @@ -9,7 +9,7 @@ import ubc.pavlab.rdp.model.enums.PrivacyLevelType; import javax.persistence.*; -import java.sql.Timestamp; +import java.time.Instant; import java.util.Optional; /** @@ -58,7 +58,7 @@ public class UserTerm extends GeneOntologyTerm implements UserContent { @CreatedDate @JsonIgnore - private Timestamp createdAt; + private Instant createdAt; public static UserTerm createUserTerm( User user, GeneOntologyTerm term, Taxon taxon ) { UserTerm userTerm = new UserTerm(); diff --git a/src/main/java/ubc/pavlab/rdp/model/VerificationToken.java b/src/main/java/ubc/pavlab/rdp/model/VerificationToken.java index 6311f853..97b8f29f 100644 --- a/src/main/java/ubc/pavlab/rdp/model/VerificationToken.java +++ b/src/main/java/ubc/pavlab/rdp/model/VerificationToken.java @@ -10,8 +10,8 @@ import ubc.pavlab.rdp.model.enums.PrivacyLevelType; import javax.persistence.*; -import java.sql.Timestamp; import java.time.Duration; +import java.time.Instant; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAmount; import java.util.Optional; @@ -44,7 +44,7 @@ public class VerificationToken extends Token implements UserContent { private String email; @CreatedDate - private Timestamp createdAt; + private Instant createdAt; @Override protected TemporalAmount getDuration() { diff --git a/src/main/java/ubc/pavlab/rdp/model/ontology/UserOntologyTerm.java b/src/main/java/ubc/pavlab/rdp/model/ontology/UserOntologyTerm.java index 8cfc6917..8e886452 100644 --- a/src/main/java/ubc/pavlab/rdp/model/ontology/UserOntologyTerm.java +++ b/src/main/java/ubc/pavlab/rdp/model/ontology/UserOntologyTerm.java @@ -14,7 +14,7 @@ import ubc.pavlab.rdp.model.enums.PrivacyLevelType; import javax.persistence.*; -import java.sql.Timestamp; +import java.time.Instant; import java.util.Optional; /** @@ -83,7 +83,7 @@ public static UserOntologyTerm fromOntologyTermInfo( User user, OntologyTermInfo @CreatedDate @JsonIgnore - private Timestamp createdAt; + private Instant createdAt; @Override @JsonIgnore diff --git a/src/main/java/ubc/pavlab/rdp/repositories/PasswordResetTokenRepository.java b/src/main/java/ubc/pavlab/rdp/repositories/PasswordResetTokenRepository.java index bde80120..c98abd9c 100644 --- a/src/main/java/ubc/pavlab/rdp/repositories/PasswordResetTokenRepository.java +++ b/src/main/java/ubc/pavlab/rdp/repositories/PasswordResetTokenRepository.java @@ -5,16 +5,14 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import ubc.pavlab.rdp.model.PasswordResetToken; -import ubc.pavlab.rdp.model.User; -import java.util.Date; -import java.util.List; +import java.time.Instant; @Repository public interface PasswordResetTokenRepository extends JpaRepository { PasswordResetToken findByToken( String token ); @Modifying - @Query("delete from PasswordResetToken t where t.expiryDate <= ?1") - void deleteAllExpiredSince( Date now ); + @Query("delete from PasswordResetToken t where t.expiryDate <= :since") + void deleteAllExpiredSince( Instant since ); } diff --git a/src/main/java/ubc/pavlab/rdp/repositories/VerificationTokenRepository.java b/src/main/java/ubc/pavlab/rdp/repositories/VerificationTokenRepository.java index f1b2f148..3a0a1c67 100644 --- a/src/main/java/ubc/pavlab/rdp/repositories/VerificationTokenRepository.java +++ b/src/main/java/ubc/pavlab/rdp/repositories/VerificationTokenRepository.java @@ -6,13 +6,13 @@ import org.springframework.stereotype.Repository; import ubc.pavlab.rdp.model.VerificationToken; -import java.sql.Timestamp; +import java.time.Instant; @Repository public interface VerificationTokenRepository extends JpaRepository { VerificationToken findByToken( String token ); @Modifying - @Query("delete from VerificationToken t where t.expiryDate <= ?1") - void deleteAllExpiredSince( Timestamp now ); + @Query("delete from VerificationToken t where t.expiryDate <= :since") + void deleteAllExpiredSince( Instant since ); } diff --git a/src/main/java/ubc/pavlab/rdp/services/EmailServiceImpl.java b/src/main/java/ubc/pavlab/rdp/services/EmailServiceImpl.java index 13ff47a7..d353f2ce 100644 --- a/src/main/java/ubc/pavlab/rdp/services/EmailServiceImpl.java +++ b/src/main/java/ubc/pavlab/rdp/services/EmailServiceImpl.java @@ -126,7 +126,7 @@ public Future sendResetTokenMessage( User user, PasswordResetToken token, Loc InternetAddress to = new InternetAddress( user.getEmail() ); String subject = messageSource.getMessage( "EmailService.sendResetTokenMessage.subject", new Object[]{ Messages.SHORTNAME }, locale ); String content = messageSource.getMessage( "EmailService.sendResetTokenMessage", new String[]{ - user.getProfile().getName(), url.toString(), dateTimeFormatter.format( token.getExpiryDate().toInstant() ) }, locale ); + user.getProfile().getName(), url.toString(), dateTimeFormatter.format( token.getExpiryDate() ) }, locale ); return sendSimpleMessage( subject, content, to, null, null ); } diff --git a/src/main/java/ubc/pavlab/rdp/services/UserServiceImpl.java b/src/main/java/ubc/pavlab/rdp/services/UserServiceImpl.java index f535b2bb..30a19790 100644 --- a/src/main/java/ubc/pavlab/rdp/services/UserServiceImpl.java +++ b/src/main/java/ubc/pavlab/rdp/services/UserServiceImpl.java @@ -44,7 +44,6 @@ import javax.validation.ValidationException; import java.security.SecureRandom; -import java.sql.Timestamp; import java.text.MessageFormat; import java.time.Instant; import java.util.*; @@ -320,7 +319,7 @@ public User findUserByAccessTokenNoAuth( String accessToken ) throws TokenExcept if ( token == null ) { return null; } - if ( Instant.now().isAfter( token.getExpiryDate().toInstant() ) ) { + if ( Instant.now().isAfter( token.getExpiryDate() ) ) { // token is expired throw new ExpiredTokenException( "Token is expired." ); } @@ -814,7 +813,7 @@ public PasswordResetToken verifyPasswordResetToken( int userId, String token ) t throw new TokenException( "Password reset token is invalid." ); } - if ( Instant.now().isAfter( passToken.getExpiryDate().toInstant() ) ) { + if ( Instant.now().isAfter( passToken.getExpiryDate() ) ) { throw new TokenException( "Password reset token is expired." ); } @@ -869,7 +868,7 @@ public User confirmVerificationToken( String token ) throws TokenException { throw new TokenException( "Verification token is invalid." ); } - if ( Instant.now().isAfter( verificationToken.getExpiryDate().toInstant() ) ) { + if ( Instant.now().isAfter( verificationToken.getExpiryDate() ) ) { throw new TokenException( "Verification token is expired." ); } @@ -879,13 +878,13 @@ public User confirmVerificationToken( String token ) throws TokenException { if ( verificationToken.getEmail().equals( user.getEmail() ) ) { user.setEnabled( true ); - user.setEnabledAt( Timestamp.from( Instant.now() ) ); + user.setEnabledAt( Instant.now() ); tokenUsed = true; } if ( user.getProfile().getContactEmail() != null && verificationToken.getEmail().equals( user.getProfile().getContactEmail() ) ) { user.getProfile().setContactEmailVerified( true ); - user.getProfile().setContactEmailVerifiedAt( Timestamp.from( Instant.now() ) ); + user.getProfile().setContactEmailVerifiedAt( Instant.now() ); tokenUsed = true; } diff --git a/src/main/java/ubc/pavlab/rdp/util/GeneInfoParser.java b/src/main/java/ubc/pavlab/rdp/util/GeneInfoParser.java index 891d2bb3..d596594d 100644 --- a/src/main/java/ubc/pavlab/rdp/util/GeneInfoParser.java +++ b/src/main/java/ubc/pavlab/rdp/util/GeneInfoParser.java @@ -7,10 +7,10 @@ import org.springframework.stereotype.Component; import java.io.*; -import java.text.DateFormat; import java.text.MessageFormat; -import java.text.SimpleDateFormat; -import java.util.Date; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.List; import java.util.stream.Collectors; @@ -27,7 +27,7 @@ public class GeneInfoParser { private static final String[] EXPECTED_HEADER_FIELDS = { "#tax_id", "GeneID", "Symbol", "Synonyms", "description", "Modification_date" }; - private static final DateFormat NCBI_DATE_FORMAT = new SimpleDateFormat( "yyyyMMdd" ); + private static final DateTimeFormatter NCBI_DATE_FORMAT = DateTimeFormatter.ofPattern( "yyyyMMdd" ); public List parse( InputStream input, Integer taxonId ) throws ParseException, IOException { try ( LineNumberReader br = new LineNumberReader( new InputStreamReader( input ) ) ) { @@ -67,7 +67,7 @@ public static class Record { private String symbol; private String synonyms; private String description; - private Date modificationDate; + private LocalDate modificationDate; public static Record parseLine( String line, String[] header, int lineNumber ) throws UncheckedParseException { String[] values = line.split( "\t" ); @@ -89,10 +89,10 @@ public static Record parseLine( String line, String[] header, int lineNumber ) t String symbol = values[indexOf( header, "Symbol" )]; String synonyms = values[indexOf( header, "Synonyms" )]; String description = values[indexOf( header, "description" )]; - Date modificationDate = null; + LocalDate modificationDate = null; try { - modificationDate = NCBI_DATE_FORMAT.parse( values[indexOf( header, "Modification_date" )] ); - } catch ( java.text.ParseException e ) { + modificationDate = LocalDate.parse( values[indexOf( header, "Modification_date" )], NCBI_DATE_FORMAT ); + } catch ( DateTimeParseException e ) { log.warn( MessageFormat.format( "Malformed date for gene {0} in taxon {1}, value will be ignored.", geneId, taxonId ), e ); } return new Record( taxonId, geneId, symbol, synonyms, description, modificationDate ); diff --git a/src/main/resources/templates/admin/user.html b/src/main/resources/templates/admin/user.html index 2254353b..3547f05a 100644 --- a/src/main/resources/templates/admin/user.html +++ b/src/main/resources/templates/admin/user.html @@ -34,7 +34,7 @@

User details

Yes, on . + th:replace="fragments/temporal::date-and-time(${user.enabledAt})">. No
@@ -45,7 +45,7 @@

User details


Verified on - . + .
diff --git a/src/main/resources/templates/search/user.html b/src/main/resources/templates/search/user.html index 7891997a..5519d845 100644 --- a/src/main/resources/templates/search/user.html +++ b/src/main/resources/templates/search/user.html @@ -309,7 +309,7 @@

Data Privacy

Last modified on - . + .

diff --git a/src/test/java/ubc/pavlab/rdp/repositories/PasswordResetTokenRepositoryTest.java b/src/test/java/ubc/pavlab/rdp/repositories/PasswordResetTokenRepositoryTest.java index a63c134a..535d5532 100644 --- a/src/test/java/ubc/pavlab/rdp/repositories/PasswordResetTokenRepositoryTest.java +++ b/src/test/java/ubc/pavlab/rdp/repositories/PasswordResetTokenRepositoryTest.java @@ -12,9 +12,8 @@ import ubc.pavlab.rdp.model.PasswordResetToken; import ubc.pavlab.rdp.model.User; -import java.sql.Timestamp; import java.time.Instant; -import java.util.Date; +import java.time.temporal.ChronoUnit; import static org.assertj.core.api.Assertions.assertThat; import static ubc.pavlab.rdp.util.TestUtils.createUnpersistedUser; @@ -45,16 +44,18 @@ public void setUp() { validToken.updateToken( "validtoken" ); validToken.setUser( user ); entityManager.persist( validToken ); - assertThat( validToken.getCreatedAt() ).isCloseTo( Instant.now(), 500 ); + assertThat( validToken.getCreatedAt() ) + .isBetween( Instant.now().minus( 500, ChronoUnit.MILLIS ), Instant.now() ); User user2 = entityManager.persistAndFlush( createUnpersistedUser() ); expiredToken = new PasswordResetToken(); expiredToken.setToken( "expiredtoken" ); expiredToken.setUser( user2 ); - expiredToken.setExpiryDate( Timestamp.from( Instant.now() ) ); + expiredToken.setExpiryDate( Instant.now() ); expiredToken = entityManager.persistAndFlush( expiredToken ); - assertThat( expiredToken.getCreatedAt() ).isCloseTo( Instant.now(), 500 ); + assertThat( expiredToken.getCreatedAt() ) + .isBetween( Instant.now().minus( 500, ChronoUnit.MILLIS ), Instant.now() ); } @Test @@ -80,7 +81,7 @@ public void findByToken_whenInvalidToken_thenReturnNull() { @Test public void deleteAllExpiredSince_whenValidDate_thenDeleteTokens() { - passwordResetTokenRepository.deleteAllExpiredSince( new Date() ); + passwordResetTokenRepository.deleteAllExpiredSince( Instant.now() ); assertThat( passwordResetTokenRepository.findAll() ).containsExactly( validToken ); } diff --git a/src/test/java/ubc/pavlab/rdp/repositories/UserGeneRepositoryTest.java b/src/test/java/ubc/pavlab/rdp/repositories/UserGeneRepositoryTest.java index e9219320..e4e0b91e 100644 --- a/src/test/java/ubc/pavlab/rdp/repositories/UserGeneRepositoryTest.java +++ b/src/test/java/ubc/pavlab/rdp/repositories/UserGeneRepositoryTest.java @@ -17,8 +17,8 @@ import ubc.pavlab.rdp.model.enums.PrivacyLevelType; import ubc.pavlab.rdp.model.enums.TierType; -import java.sql.Timestamp; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; @@ -47,10 +47,13 @@ public class UserGeneRepositoryTest { public void setUp() { taxon = entityManager.persistAndFlush( createTaxon( 1 ) ); user = entityManager.persistAndFlush( createUserWithGenes( taxon ) ); + Instant a = Instant.now().minus( 500, ChronoUnit.MILLIS ); + Instant b = Instant.now(); assertThat( user.getUserGenes() ) .isNotEmpty() .allSatisfy( ( geneId, userGene ) -> { - assertThat( userGene.getCreatedAt() ).isCloseTo( Instant.now(), 500 ); + assertThat( userGene.getCreatedAt() ) + .isBetween( a, b ); } ); } @@ -231,7 +234,7 @@ public void countDistinctUser_whenMultipleMatchesDifferentTaxon_thenCountDistinc public void findByUserEnabledTrue() { Gene gene = entityManager.persist( createGene( 1, taxon ) ); user.setEnabled( true ); - user.setEnabledAt( Timestamp.from( Instant.now() ) ); + user.setEnabledAt( Instant.now() ); user.getProfile().setPrivacyLevel( PrivacyLevelType.PUBLIC ); user.getUserGenes().clear(); user = entityManager.persistAndFlush( user ); @@ -260,7 +263,7 @@ public void findAllByPrivacyLevelAndUserProfilePrivacyLevel() { Gene gene2 = entityManager.persist( createGene( 2, taxon ) ); Gene gene3 = entityManager.persist( createGene( 3, taxon ) ); user.setEnabled( true ); - user.setEnabledAt( Timestamp.from( Instant.now() ) ); + user.setEnabledAt( Instant.now() ); user.getProfile().setPrivacyLevel( PrivacyLevelType.PUBLIC ); user.getUserGenes().clear(); user = entityManager.persistAndFlush( user ); @@ -281,7 +284,7 @@ public void findAllByPrivacyLevelAndUserProfilePrivacyLevel_whenProfileIsPrivate Gene gene2 = entityManager.persist( createGene( 2, taxon ) ); Gene gene3 = entityManager.persist( createGene( 3, taxon ) ); user.setEnabled( true ); - user.setEnabledAt( Timestamp.from( Instant.now() ) ); + user.setEnabledAt( Instant.now() ); user.getProfile().setPrivacyLevel( PrivacyLevelType.PRIVATE ); user.getUserGenes().clear(); user = entityManager.persistAndFlush( user ); @@ -298,7 +301,7 @@ public void findAllByPrivacyLevelAndUserProfilePrivacyLevel_whenProfileIsPrivate public void findAllByPrivacyLevelAndUserProfilePrivacyLevel_whenGenePrivacyLevelIsNull_thenFallbackOnProfile() { Gene gene = entityManager.persist( createGene( 1, taxon ) ); user.setEnabled( true ); - user.setEnabledAt( Timestamp.from( Instant.now() ) ); + user.setEnabledAt( Instant.now() ); user.getProfile().setPrivacyLevel( PrivacyLevelType.PUBLIC ); user.getUserGenes().clear(); user = entityManager.persistAndFlush( user ); diff --git a/src/test/java/ubc/pavlab/rdp/repositories/UserOrganRepositoryTest.java b/src/test/java/ubc/pavlab/rdp/repositories/UserOrganRepositoryTest.java index e6391d44..773f07b4 100644 --- a/src/test/java/ubc/pavlab/rdp/repositories/UserOrganRepositoryTest.java +++ b/src/test/java/ubc/pavlab/rdp/repositories/UserOrganRepositoryTest.java @@ -13,6 +13,7 @@ import ubc.pavlab.rdp.model.UserOrgan; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Collection; import static org.assertj.core.api.Assertions.assertThat; @@ -44,7 +45,7 @@ private UserOrgan createUserOrgan( User user, String uberonId, String name, Stri public void setUp() { User user = entityManager.persistAndFlush( createUnpersistedUser() ); userOrgan = entityManager.persistAndFlush( createUserOrgan( user, "UBERON_....", "Limb/Appendage", "Limb or appendage" ) ); - assertThat( userOrgan.getCreatedAt() ).isCloseTo( Instant.now(), 500 ); + assertThat( userOrgan.getCreatedAt() ).isBetween( Instant.now().minus( 500, ChronoUnit.MILLIS ), Instant.now() ); } @Test diff --git a/src/test/java/ubc/pavlab/rdp/repositories/UserRepositoryTest.java b/src/test/java/ubc/pavlab/rdp/repositories/UserRepositoryTest.java index 87c1fd01..2069c806 100644 --- a/src/test/java/ubc/pavlab/rdp/repositories/UserRepositoryTest.java +++ b/src/test/java/ubc/pavlab/rdp/repositories/UserRepositoryTest.java @@ -15,6 +15,7 @@ import ubc.pavlab.rdp.model.enums.TierType; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.EnumSet; @@ -495,7 +496,8 @@ public void save_whenUserWithCompleteProfile_thenSucceed() { user.getProfile().setResearcherPosition( ResearcherPosition.PRINCIPAL_INVESTIGATOR ); user.getProfile().getResearcherCategories().add( ResearcherCategory.IN_SILICO ); User persistedUser = entityManager.persistAndFlush( user ); - assertThat( persistedUser.getCreatedAt() ).isCloseTo( Instant.now(), 500 ); + assertThat( persistedUser.getCreatedAt() ) + .isBetween( Instant.now().minus( 500, ChronoUnit.MILLIS ), Instant.now() ); assertThat( persistedUser.getProfile() ) .hasFieldOrPropertyWithValue( "researcherPosition", ResearcherPosition.PRINCIPAL_INVESTIGATOR ) .hasFieldOrPropertyWithValue( "researcherCategories", EnumSet.of( ResearcherCategory.IN_SILICO ) ); diff --git a/src/test/java/ubc/pavlab/rdp/repositories/VerificationTokenRepositoryTest.java b/src/test/java/ubc/pavlab/rdp/repositories/VerificationTokenRepositoryTest.java index 74690c3a..0e224c4b 100644 --- a/src/test/java/ubc/pavlab/rdp/repositories/VerificationTokenRepositoryTest.java +++ b/src/test/java/ubc/pavlab/rdp/repositories/VerificationTokenRepositoryTest.java @@ -1,5 +1,6 @@ package ubc.pavlab.rdp.repositories; +import org.assertj.core.data.TemporalUnitOffset; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -12,8 +13,8 @@ import ubc.pavlab.rdp.model.User; import ubc.pavlab.rdp.model.VerificationToken; -import java.sql.Timestamp; import java.time.Instant; +import java.time.temporal.ChronoUnit; import static org.assertj.core.api.Assertions.assertThat; import static ubc.pavlab.rdp.util.TestUtils.createUnpersistedUser; @@ -45,7 +46,8 @@ public void setUp() { validToken.setUser( user ); validToken.setEmail( user.getEmail() ); validToken = entityManager.persistAndFlush( validToken ); - assertThat( validToken.getCreatedAt() ).isCloseTo( Instant.now(), 500 ); + assertThat( validToken.getCreatedAt() ) + .isBetween( Instant.now().minus( 500, ChronoUnit.MILLIS ), Instant.now() ); User user2 = entityManager.persistAndFlush( createUnpersistedUser() ); @@ -53,9 +55,10 @@ public void setUp() { expiredToken.setToken( "expiredtoken" ); expiredToken.setUser( user2 ); expiredToken.setEmail( user2.getEmail() ); - expiredToken.setExpiryDate( Timestamp.from( Instant.now() ) ); + expiredToken.setExpiryDate( Instant.now() ); expiredToken = entityManager.persistAndFlush( expiredToken ); - assertThat( expiredToken.getCreatedAt() ).isCloseTo( Instant.now(), 500 ); + assertThat( expiredToken.getCreatedAt() ) + .isBetween( Instant.now().minus( 500, ChronoUnit.MILLIS ), Instant.now() ); } @Test @@ -81,7 +84,7 @@ public void findByToken_whenInvalidToken_thenReturnNull() { @Test public void deleteAllExpiredSince_whenValidDate_thenDeleteTokens() { - verificationTokenRepository.deleteAllExpiredSince( Timestamp.from( Instant.now() ) ); + verificationTokenRepository.deleteAllExpiredSince( Instant.now() ); assertThat( verificationTokenRepository.findAll() ).containsExactly( validToken ); } diff --git a/src/test/java/ubc/pavlab/rdp/repositories/ontology/UserOntologyRepositoryTest.java b/src/test/java/ubc/pavlab/rdp/repositories/ontology/UserOntologyRepositoryTest.java index 38f5d63f..06911520 100644 --- a/src/test/java/ubc/pavlab/rdp/repositories/ontology/UserOntologyRepositoryTest.java +++ b/src/test/java/ubc/pavlab/rdp/repositories/ontology/UserOntologyRepositoryTest.java @@ -15,6 +15,7 @@ import javax.persistence.EntityManager; import java.time.Instant; +import java.time.temporal.ChronoUnit; import static org.assertj.core.api.Assertions.assertThat; import static ubc.pavlab.rdp.util.TestUtils.createUnpersistedUser; @@ -52,8 +53,10 @@ public void delete_whenUserHasTerm_thenSetTermInfoToNull() { UserOntologyTerm uo = UserOntologyTerm.fromOntologyTermInfo( user, term ); user.getUserOntologyTerms().add( uo ); user = userRepository.saveAndFlush( user ); + Instant a = Instant.now().minus( 500, ChronoUnit.MILLIS ); + Instant b = Instant.now(); assertThat( user.getUserOntologyTerms() ).allSatisfy( userTerm -> { - assertThat( userTerm.getCreatedAt() ).isCloseTo( Instant.now(), 500 ); + assertThat( userTerm.getCreatedAt() ).isBetween( a, b ); } ); ontology.getTerms().remove( term ); diff --git a/src/test/java/ubc/pavlab/rdp/security/PrivacyServiceImplTest.java b/src/test/java/ubc/pavlab/rdp/security/PrivacyServiceImplTest.java index 34b78463..fe19ab93 100644 --- a/src/test/java/ubc/pavlab/rdp/security/PrivacyServiceImplTest.java +++ b/src/test/java/ubc/pavlab/rdp/security/PrivacyServiceImplTest.java @@ -19,7 +19,6 @@ import ubc.pavlab.rdp.services.UserService; import ubc.pavlab.rdp.settings.ApplicationSettings; -import java.sql.Timestamp; import java.time.Instant; import java.util.Optional; @@ -72,10 +71,10 @@ public void setUp() { Role roleServiceAccount = createRole( 4, "ROLE_SERVICE_ACCOUNT" ); user = createUser( 1 ); user.setEnabled( true ); - user.setEnabledAt( Timestamp.from( Instant.now() ) ); + user.setEnabledAt( Instant.now() ); otherUser = createUser( 2 ); otherUser.setEnabled( true ); - otherUser.setEnabledAt( Timestamp.from( Instant.now() ) ); + otherUser.setEnabledAt( Instant.now() ); adminUser = createUserWithRoles( 3, roleAdmin ); serviceAccountUser = createUserWithRoles( 4, roleServiceAccount ); when( roleRepository.findByRole( "ROLE_ADMIN" ) ).thenReturn( roleAdmin ); diff --git a/src/test/java/ubc/pavlab/rdp/services/EmailServiceImplTest.java b/src/test/java/ubc/pavlab/rdp/services/EmailServiceImplTest.java index 1c2f757c..9639ccf3 100644 --- a/src/test/java/ubc/pavlab/rdp/services/EmailServiceImplTest.java +++ b/src/test/java/ubc/pavlab/rdp/services/EmailServiceImplTest.java @@ -24,7 +24,6 @@ import javax.mail.MessagingException; import java.net.URI; -import java.sql.Timestamp; import java.time.Instant; import java.util.Locale; import java.util.concurrent.ExecutionException; @@ -85,7 +84,7 @@ public void sendUserRegistered_thenSucceed() throws MessagingException { public void sendSupportMessage_thenSucceed() throws MessagingException { User user = createUser( 1 ); user.setEnabled( true ); - user.setEnabledAt( Timestamp.from( Instant.now() ) ); + user.setEnabledAt( Instant.now() ); assertThat( emailService.sendSupportMessage( "I need help!", "John Doe", user, "Google Chrome", null, Locale.getDefault() ) ) .succeedsWithin( 1, TimeUnit.SECONDS ); ArgumentCaptor mailMessageCaptor = ArgumentCaptor.forClass( SimpleMailMessage.class ); @@ -173,11 +172,11 @@ public void sendUserGeneAccessRequest_thenSucceed() throws MessagingException { User user = createUser( 1 ); user.getProfile().setContactEmail( "foo@example.com" ); user.getProfile().setContactEmailVerified( true ); - user.getProfile().setContactEmailVerifiedAt( Timestamp.from( Instant.now() ) ); + user.getProfile().setContactEmailVerifiedAt( Instant.now() ); User user2 = createUser( 2 ); user2.getProfile().setContactEmail( "bar@example.com" ); user2.getProfile().setContactEmailVerified( true ); - user2.getProfile().setContactEmailVerifiedAt( Timestamp.from( Instant.now() ) ); + user2.getProfile().setContactEmailVerifiedAt( Instant.now() ); UserGene userGene = createUserGene( 1, createGene( 1, createTaxon( 1 ) ), user2, TierType.TIER1, PrivacyLevelType.PRIVATE ); assertThat( emailService.sendUserGeneAccessRequest( userGene, user, "Because." ) ) .succeedsWithin( 1, TimeUnit.SECONDS ); diff --git a/src/test/java/ubc/pavlab/rdp/services/GeneInfoServiceImplTest.java b/src/test/java/ubc/pavlab/rdp/services/GeneInfoServiceImplTest.java index aee8c26f..6f53b81c 100644 --- a/src/test/java/ubc/pavlab/rdp/services/GeneInfoServiceImplTest.java +++ b/src/test/java/ubc/pavlab/rdp/services/GeneInfoServiceImplTest.java @@ -27,8 +27,8 @@ import java.io.IOException; import java.net.URL; -import java.sql.Date; import java.time.Instant; +import java.time.LocalDate; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -297,7 +297,7 @@ public void autocomplete_whenMultipleMatchesAndResultsUnlimited_thenReturnAll() public void updateGenes_thenSucceed() throws ParseException, IOException { Taxon humanTaxon = taxonRepository.findById( 9606 ).get(); when( taxonService.findByActiveTrue() ).thenReturn( Sets.newSet( humanTaxon ) ); - GeneInfoParser.Record updatedGeneRecord = new GeneInfoParser.Record( 9606, 4, "FOO", "", "BAR", Date.from( Instant.now() ) ); + GeneInfoParser.Record updatedGeneRecord = new GeneInfoParser.Record( 9606, 4, "FOO", "", "BAR", LocalDate.now() ); when( geneInfoParser.parse( any(), eq( humanTaxon.getId() ) ) ).thenReturn( Collections.singletonList( updatedGeneRecord ) ); geneService.updateGenes(); verify( taxonService ).findByActiveTrue(); @@ -313,7 +313,7 @@ public void updateGenes_thenSucceed() throws ParseException, IOException { public void updateGenes_whenTaxonDiffers_thenIgnoreGeneRecord() throws ParseException, IOException { Taxon humanTaxon = taxonRepository.findById( 9606 ).get(); when( taxonService.findByActiveTrue() ).thenReturn( Sets.newSet( humanTaxon ) ); - GeneInfoParser.Record updatedGeneRecord = new GeneInfoParser.Record( 4, 4, "FOO", "", "BAR", Date.from( Instant.now() ) ); + GeneInfoParser.Record updatedGeneRecord = new GeneInfoParser.Record( 4, 4, "FOO", "", "BAR", LocalDate.now() ); when( geneInfoParser.parse( any(), eq( 4 ) ) ).thenReturn( Collections.singletonList( updatedGeneRecord ) ); geneService.updateGenes(); verify( taxonService ).findByActiveTrue(); @@ -327,7 +327,7 @@ public void updateGenes_whenTaxonIsChanged_thenUpdateGene() throws ParseExceptio Taxon otherTaxon = entityManager.persist( createTaxon( 4 ) ); when( taxonService.findByActiveTrue() ).thenReturn( Sets.newSet( humanTaxon ) ); entityManager.persist( createGene( 4, otherTaxon ) ); - GeneInfoParser.Record updatedGeneRecord = new GeneInfoParser.Record( 9606, 4, "FOO", "", "BAR", Date.from( Instant.now() ) ); + GeneInfoParser.Record updatedGeneRecord = new GeneInfoParser.Record( 9606, 4, "FOO", "", "BAR", LocalDate.now() ); when( geneInfoParser.parse( any(), eq( humanTaxon.getId() ) ) ).thenReturn( Collections.singletonList( updatedGeneRecord ) ); geneService.updateGenes(); verify( taxonService ).findByActiveTrue(); @@ -342,8 +342,8 @@ public void updateGenes_whenTaxonIsChanged_thenUpdateGene() throws ParseExceptio public void updateGenes_whenGeneInfoContainsMultipleTaxon_thenIgnoreUnrelatedTaxon() throws ParseException, IOException { Taxon fissionYeastTaxon = createTaxon( 4896, "fission yeast", "Schizosaccharomyces pombe", new URL( "https://ftp.ncbi.nih.gov/gene/DATA/GENE_INFO/Fungi/All_Fungi.gene_info.gz" ) ); when( taxonService.findByActiveTrue() ).thenReturn( Sets.newSet( fissionYeastTaxon ) ); - GeneInfoParser.Record updatedGeneRecord = new GeneInfoParser.Record( 4896, 4, "FOO", "", "BAR", Date.from( Instant.now() ) ); - GeneInfoParser.Record unrelatedGeneRecord = new GeneInfoParser.Record( 12, 4, "FOO", "", "BAR", Date.from( Instant.now() ) ); + GeneInfoParser.Record updatedGeneRecord = new GeneInfoParser.Record( 4896, 4, "FOO", "", "BAR", LocalDate.now() ); + GeneInfoParser.Record unrelatedGeneRecord = new GeneInfoParser.Record( 12, 4, "FOO", "", "BAR", LocalDate.now() ); when( geneInfoParser.parse( any(), eq( fissionYeastTaxon.getId() ) ) ).thenReturn( Lists.newArrayList( updatedGeneRecord ) ); when( geneInfoParser.parse( any(), eq( 12 ) ) ).thenReturn( Lists.newArrayList( unrelatedGeneRecord ) ); geneService.updateGenes(); diff --git a/src/test/java/ubc/pavlab/rdp/services/UserServiceImplTest.java b/src/test/java/ubc/pavlab/rdp/services/UserServiceImplTest.java index 72a2542c..56ea6d4f 100644 --- a/src/test/java/ubc/pavlab/rdp/services/UserServiceImplTest.java +++ b/src/test/java/ubc/pavlab/rdp/services/UserServiceImplTest.java @@ -44,7 +44,6 @@ import javax.validation.ValidationException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.sql.Timestamp; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.*; @@ -199,7 +198,7 @@ private void setUpPasswordResetTokenMocks() { token = new PasswordResetToken(); token.setUser( user ); token.setToken( "token1Expired" ); - token.setExpiryDate( Timestamp.from( Instant.now().minusSeconds( 1 ) ) ); + token.setExpiryDate( Instant.now().minusSeconds( 1 ) ); when( passwordResetTokenRepository.findByToken( token.getToken() ) ).thenReturn( token ); token = new PasswordResetToken(); @@ -222,7 +221,7 @@ private void setUpVerificationTokenMocks() { token.setUser( user ); token.setEmail( user.getEmail() ); token.setToken( "token1Expired" ); - token.setExpiryDate( Timestamp.from( Instant.now().minus( 1, ChronoUnit.SECONDS ) ) ); + token.setExpiryDate( Instant.now().minus( 1, ChronoUnit.SECONDS ) ); when( tokenRepository.findByToken( token.getToken() ) ).thenReturn( token ); when( tokenRepository.findByToken( "tokenBad" ) ).thenReturn( null ); @@ -664,7 +663,7 @@ public void updateUserProfileAndPublicationsAndOrgans_whenContactEmailIsNull_the User user = createUser( 1 ); user.getProfile().setContactEmail( "foo@example.com" ); user.getProfile().setContactEmailVerified( true ); - user.getProfile().setContactEmailVerifiedAt( Timestamp.from( Instant.now() ) ); + user.getProfile().setContactEmailVerifiedAt( Instant.now() ); Profile profile = new Profile(); profile.setPrivacyLevel( PrivacyLevelType.PUBLIC ); profile.setContactEmail( null ); @@ -680,7 +679,7 @@ public void updateUserProfileAndPublicationsAndOrgans_whenContactEmailIsEmpty_th User user = createUser( 1 ); user.getProfile().setContactEmail( "foo@example.com" ); user.getProfile().setContactEmailVerified( true ); - user.getProfile().setContactEmailVerifiedAt( Timestamp.from( Instant.now() ) ); + user.getProfile().setContactEmailVerifiedAt( Instant.now() ); Profile profile = new Profile(); profile.setPrivacyLevel( PrivacyLevelType.PUBLIC ); profile.setContactEmail( "" ); @@ -726,7 +725,7 @@ public void createPasswordResetTokenForUser_hasCorrectExpiration() { Instant upperBound = Instant.now().plus( 2, ChronoUnit.HOURS ).plus( 1, ChronoUnit.MINUTES ); // one minute tolerance - assertThat( passwordResetToken.getExpiryDate().toInstant() ).isBetween( lowerBound, upperBound ); + assertThat( passwordResetToken.getExpiryDate() ).isBetween( lowerBound, upperBound ); verify( userListener ).onUserPasswordReset( new OnUserPasswordResetEvent( user, passwordResetToken, Locale.getDefault() ) ); } @@ -768,7 +767,7 @@ public void createVerificationTokenForUser_hasCorrectExpiration() { Instant upperBound = Instant.now().plus( 24, ChronoUnit.HOURS ).plus( 1, ChronoUnit.MINUTES ); // one minute tolerance - assertThat( verificationToken.getExpiryDate().toInstant() ).isBetween( lowerBound, upperBound ); + assertThat( verificationToken.getExpiryDate() ).isBetween( lowerBound, upperBound ); verify( userListener ).onRegistrationComplete( any( OnRegistrationCompleteEvent.class ) ); } @@ -1308,7 +1307,7 @@ public void anonymizeUser_thenReturnAnonymizedUser() { when( privacySettings.isEnableAnonymizedSearchResults() ).thenReturn( true ); User user = createUser( 1 ); user.setEnabled( true ); - user.setEnabledAt( Timestamp.from( Instant.now() ) ); + user.setEnabledAt( Instant.now() ); user.getProfile().setPrivacyLevel( PrivacyLevelType.PRIVATE ); User anonymizedUser = userService.anonymizeUser( user ); assertThat( anonymizedUser ) diff --git a/src/test/java/ubc/pavlab/rdp/util/TestUtils.java b/src/test/java/ubc/pavlab/rdp/util/TestUtils.java index 12fa2bc6..b827b552 100644 --- a/src/test/java/ubc/pavlab/rdp/util/TestUtils.java +++ b/src/test/java/ubc/pavlab/rdp/util/TestUtils.java @@ -16,7 +16,6 @@ import java.net.URI; import java.net.URL; -import java.sql.Timestamp; import java.time.Instant; import java.util.*; import java.util.function.Function; @@ -73,7 +72,7 @@ public static User createUnpersistedUser() { .email( String.format( EMAIL_FORMAT, emailCount++ ) ) .password( ENCODED_PASSWORD ) // imbatman .enabled( true ) - .enabledAt( Timestamp.from( Instant.now() ) ) + .enabledAt( Instant.now() ) .build(); } From 833833c1b06b8c580e30e69d4f97fb419d3b6185 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Tue, 22 Nov 2022 15:15:04 -0800 Subject: [PATCH 33/34] Use JdbcTemplate in MigrationConfig --- .../java/ubc/pavlab/rdp/MigrationConfig.java | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/main/java/ubc/pavlab/rdp/MigrationConfig.java b/src/main/java/ubc/pavlab/rdp/MigrationConfig.java index df665e8b..0f9c62e9 100644 --- a/src/main/java/ubc/pavlab/rdp/MigrationConfig.java +++ b/src/main/java/ubc/pavlab/rdp/MigrationConfig.java @@ -6,10 +6,10 @@ import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.ConnectionCallback; +import org.springframework.jdbc.core.JdbcTemplate; -import java.sql.Connection; import java.sql.ResultSet; -import java.sql.SQLException; import java.util.Arrays; /** @@ -67,20 +67,17 @@ private static class ExpectedMigrationInfo { @Bean public FlywayMigrationStrategy flywayMigrationStrategy() { return flyway -> { - try { - // drop version_rank column - Connection connection = flyway.getConfiguration().getDataSource().getConnection(); - // lookup the schema_version table and version_rank column - boolean hasVersionRankColumn; - try ( ResultSet resultSet = connection.getMetaData().getColumns( null, null, "schema_version", "version_rank" ) ) { - hasVersionRankColumn = resultSet.next(); + // drop version_rank column + JdbcTemplate jdbcTemplate = new JdbcTemplate( flyway.getConfiguration().getDataSource() ); + // lookup the schema_version table and version_rank column + Boolean hasVersionRankColumn = jdbcTemplate.execute( (ConnectionCallback) con -> { + try ( ResultSet resultSet = con.getMetaData().getColumns( null, null, "schema_version", "version_rank" ) ) { + return resultSet.next(); } - if ( hasVersionRankColumn ) { - log.warn( "The 'schema_version' table is still using the 'version_rank' column from Flyway 3.2.1; will proceed to remove it..." ); - connection.createStatement().execute( "alter table schema_version drop column version_rank" ); - } - } catch ( SQLException e ) { - throw new RuntimeException( e ); + } ); + if ( hasVersionRankColumn != null && hasVersionRankColumn ) { + log.warn( "The 'schema_version' table is still using the 'version_rank' column from Flyway 3.2.1; will proceed to remove it..." ); + jdbcTemplate.execute( "alter table schema_version drop column version_rank" ); } MigrationInfo[] appliedMigrations = flyway.info().applied(); boolean repairNeeded = false; From 92d27c1c15d9788e56a47908199b1097f6950739 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier-Morency Date: Tue, 22 Nov 2022 15:30:23 -0800 Subject: [PATCH 34/34] Add missing @Configuration annotation to JpaAuditingConfig --- src/main/java/ubc/pavlab/rdp/JpaAuditingConfig.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/ubc/pavlab/rdp/JpaAuditingConfig.java b/src/main/java/ubc/pavlab/rdp/JpaAuditingConfig.java index fec194c6..f5e7d74b 100644 --- a/src/main/java/ubc/pavlab/rdp/JpaAuditingConfig.java +++ b/src/main/java/ubc/pavlab/rdp/JpaAuditingConfig.java @@ -1,5 +1,6 @@ package ubc.pavlab.rdp; +import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; /** @@ -7,6 +8,7 @@ * * @author poirigui */ +@Configuration @EnableJpaAuditing public class JpaAuditingConfig { }