migrate project to correct database scheme

develop
bvn13 2020-04-19 02:40:57 +03:00
parent c22e9c13d8
commit 6e469b2d67
23 changed files with 369 additions and 69 deletions

View File

@ -30,6 +30,8 @@ subprojects {
fasterxmlJacksonVersion = '2.10.0'
apacheCommonsCollectionsVersion = '4.4'
apacheCommonsLangVersion = '3.9'
jsoupVersion = '1.13.1'
flywaydbVersion = '6.3.3'
}
group = 'com.bvn13.covid19'

View File

@ -34,8 +34,8 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
@RestController
@RequestMapping("/")
public class MainController {
@RequestMapping("/api")
public class ApiController {
private final CovidStatsMaker covidStatsMaker;

View File

@ -17,7 +17,7 @@ limitations under the License.
package com.bvn13.covid19.api.repositories;
import com.bvn13.covid19.model.entities.CovidStat;
import com.bvn13.covid19.model.entities.CovidUpdateInfo;
import com.bvn13.covid19.model.entities.CovidUpdate;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@ -26,7 +26,7 @@ import java.util.Collection;
@Repository
public interface CovidStatsRepository extends JpaRepository<CovidStat, Long> {
Collection<CovidStat> findAllByUpdateInfo(CovidUpdateInfo updateInfo);
Collection<CovidStat> findAllByUpdateInfo(CovidUpdate updateInfo);
Collection<CovidStat> findAllByUpdateInfo_Id(long updateInfoId);
}

View File

@ -17,7 +17,7 @@ limitations under the License.
package com.bvn13.covid19.api.repositories;
import com.bvn13.covid19.api.dtos.CovidUpdateInfoDto;
import com.bvn13.covid19.model.entities.CovidUpdateInfo;
import com.bvn13.covid19.model.entities.CovidUpdate;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
@ -26,11 +26,11 @@ import java.time.LocalDateTime;
import java.util.Optional;
@Repository
public interface CovidUpdateInfosRepository extends JpaRepository<CovidUpdateInfo, Long> {
public interface CovidUpdateInfosRepository extends JpaRepository<CovidUpdate, Long> {
@Query("select new com.bvn13.covid19.api.dtos.CovidUpdateInfoDto(max(U.createdOn)) from CovidUpdateInfo U")
@Query("select new com.bvn13.covid19.api.dtos.CovidUpdateInfoDto(max(U.createdOn)) from CovidUpdate U")
Optional<CovidUpdateInfoDto> findLastUpdateInfo();
Optional<CovidUpdateInfo> findFirstByCreatedOn(LocalDateTime createdOn);
Optional<CovidUpdate> findFirstByCreatedOn(LocalDateTime createdOn);
}

View File

@ -19,7 +19,7 @@ package com.bvn13.covid19.api.service;
import com.bvn13.covid19.api.repositories.CovidStatsRepository;
import com.bvn13.covid19.api.repositories.CovidUpdateInfosRepository;
import com.bvn13.covid19.model.entities.CovidStat;
import com.bvn13.covid19.model.entities.CovidUpdateInfo;
import com.bvn13.covid19.model.entities.CovidUpdate;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
@ -49,7 +49,7 @@ public class CovidStatsMaker {
unless = "#result == null"
)
@Transactional
public Optional<CovidUpdateInfo> findLastUpdateInfo() {
public Optional<CovidUpdate> findLastUpdateInfo() {
return updatesRepository.findLastUpdateInfo()
.flatMap(updateInfo -> updatesRepository.findFirstByCreatedOn(updateInfo.getCreatedOn()));
}

View File

@ -1,3 +1,6 @@
server:
port: 8080
app:
zone-id: Europe/Moscow
@ -11,6 +14,10 @@ spring:
spec: expireAfterWrite=15m
cache-names: covid-last-update-info, covid-stats-by-update-info-id
flyway:
locations: classpath:db/migration
schemas: covid
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/covid19
@ -21,10 +28,11 @@ spring:
show-sql: true
hibernate:
dialect: org.hibernate.dialect.PostgreSQL
ddl-auto: update
ddl-auto: validate
properties:
hibernate:
default_schema: covid
use_sql_comments: true
format_sql: true
show_sql: true
@ -47,3 +55,4 @@ logging:
BasicBinder: trace
pattern:
console: "%d{dd-MM-yyyy HH:mm:ss.SSS} %highlight(%-5level) %magenta([%thread]) %yellow(%logger.%M) - %msg%n"

View File

@ -5,6 +5,10 @@ version = '0.0.1'
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
runtimeOnly 'org.postgresql:postgresql'
implementation 'org.flywaydb:flyway-core'
testImplementation 'com.h2database:h2:1.4.194'
}

View File

@ -1,14 +1,25 @@
/*
Copyright [2020] [bvn13]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.bvn13.covid19.model;
import com.bvn13.covid19.model.Covid19ModelConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
//@SpringBootApplication
//@Import({
// Covid19ModelConfig.class
//})
@SpringBootApplication
public class Covid19ModelApplication {
public static void main(String[] args) {

View File

@ -19,7 +19,6 @@ package com.bvn13.covid19.model;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@Configuration
@ComponentScan("com.bvn13.covid19.model")

View File

@ -23,11 +23,12 @@ import java.time.LocalDateTime;
@Data
@Entity
@Table(name = "covid_statistics")
@Table(schema = "covid", name = "cvd_stats")
public class CovidStat {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@GeneratedValue(strategy = GenerationType.IDENTITY, generator = "cvd_stats_seq")
@SequenceGenerator(schema = "covid", name = "cvd_stats_seq", sequenceName = "cvd_stats_seq", allocationSize = 1)
private long id;
private LocalDateTime createdOn;
@ -37,8 +38,8 @@ public class CovidStat {
private Region region;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "update_info_id")
private CovidUpdateInfo updateInfo;
@JoinColumn(name = "update_id")
private CovidUpdate updateInfo;
private long sick;
private long healed;

View File

@ -24,13 +24,14 @@ import java.time.ZonedDateTime;
@Data
@Entity
@Table(name = "update_info", uniqueConstraints = {
@UniqueConstraint(columnNames = {"createdOn"})
@Table(schema = "covid", name = "cvd_updates", uniqueConstraints = {
@UniqueConstraint(name = "cvd_upd_created_uniq", columnNames = {"createdOn"})
})
public class CovidUpdateInfo {
public class CovidUpdate {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@GeneratedValue(strategy = GenerationType.IDENTITY, generator = "cvd_updates_seq")
@SequenceGenerator(schema = "covid", name = "cvd_updates_seq", sequenceName = "cvd_updates_seq", allocationSize = 1)
private long id;
private LocalDateTime createdOn;

View File

@ -18,19 +18,22 @@ package com.bvn13.covid19.model.entities;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
@Data
@Entity
@Table(name = "regions", uniqueConstraints = {
@UniqueConstraint(columnNames = "name")
@Table(schema = "covid", name = "regions", uniqueConstraints = {
@UniqueConstraint(name = "regions_name_uniq", columnNames = "name")
})
public class Region {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY, generator = "cvd_regions_seq")
@SequenceGenerator(schema = "covid", name = "cvd_regions_seq", sequenceName = "cvd_regions_seq", allocationSize = 1)
private long id;
@NotBlank
private String name;
}

View File

@ -1 +1,32 @@
spring:
application:
name: covid19-model
flyway:
locations: classpath:db/migration
schemas: covid
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/covid19
username: postgres
password: <postgrespass>
jpa:
show-sql: true
hibernate:
dialect: org.hibernate.dialect.PostgreSQL
ddl-auto: none
properties:
hibernate:
default_schema: covid
use_sql_comments: true
format_sql: true
show_sql: true
jdbc:
lob:
non_contextual_creation: true

View File

@ -0,0 +1,51 @@
--create schema covid;
create sequence covid.cvd_regions_seq start 1 increment by 1;
create table covid.regions
(
id bigint not null default nextval('covid.cvd_regions_seq')
constraint regions_pkey primary key,
name varchar(255) not null
constraint regions_name_uniq unique
);
alter sequence covid.cvd_regions_seq owned by covid.regions.id;
alter sequence covid.cvd_regions_seq owner to covid19;
alter table covid.regions owner to covid19;
create sequence covid.cvd_updates_seq start 1 increment by 1;
create table covid.cvd_updates
(
id bigint not null default nextval('covid.cvd_updates_seq')
constraint cvd_updates_pkey primary key,
created_on timestamp
constraint cvd_upd_created_uniq unique,
datetime timestamp
);
alter sequence covid.cvd_updates_seq owned by covid.cvd_updates.id;
alter sequence covid.cvd_updates_seq owner to covid19;
alter table covid.cvd_updates owner to covid19;
create sequence covid.cvd_stats_seq start 1 increment by 1;
create table if not exists covid.cvd_stats
(
id bigint not null default nextval('covid.cvd_stats_seq')
constraint cvd_stats_pkey primary key,
created_on timestamp,
died bigint not null,
healed bigint not null,
sick bigint not null,
region_id bigint
constraint fk_cvd_stats_region references covid.regions,
update_id bigint
constraint fk_cvd_stats_updates references covid.cvd_updates
);
alter sequence covid.cvd_stats_seq owned by covid.cvd_stats.id;
alter sequence covid.cvd_stats_seq owner to covid19;
alter table covid_statistics owner to covid19;

View File

@ -0,0 +1,104 @@
insert into covid.regions(id, name) VALUES
('1', 'Москва'),
('2', 'Московская область'),
('3', 'Санкт-Петербург'),
('4', 'Нижегородская область'),
('5', 'Республика Коми'),
('6', 'Ленинградская область'),
('7', 'Краснодарский край'),
('8', 'Мурманская область'),
('9', 'Красноярский край'),
('10', 'Республика Дагестан'),
('11', 'Брянская область'),
('12', 'Республика Башкортостан'),
('13', 'Тверская область'),
('14', 'Тульская область'),
('15', 'Республика Ингушетия'),
('16', 'Рязанская область'),
('17', 'Республика Марий Эл'),
('18', 'Ставропольский край'),
('19', 'Владимирская область'),
('20', 'Республика Мордовия'),
('21', 'Ростовская область'),
('22', 'Тюменская область'),
('23', 'Чеченская Республика'),
('24', 'Пермский край'),
('25', 'Курская область'),
('26', 'Тамбовская область'),
('27', 'Республика Чувашия'),
('28', 'Ивановская область'),
('29', 'Ханты-Мансийский АО'),
('30', 'Республика Татарстан'),
('31', 'Ульяновская область'),
('32', 'Смоленская область'),
('33', 'Калужская область'),
('34', 'Хабаровский край'),
('35', 'Пензенская область'),
('36', 'Оренбургская область'),
('37', 'Воронежская область'),
('38', 'Липецкая область'),
('39', 'Свердловская область'),
('40', 'Орловская область'),
('41', 'Калининградская область'),
('42', 'Кабардино-Балкарская Республика'),
('43', 'Республика Бурятия'),
('44', 'Кировская область'),
('45', 'Ямало-Ненецкий автономный округ'),
('46', 'Ярославская область'),
('47', 'Астраханская область'),
('48', 'Саратовская область'),
('49', 'Республика Северная Осетия — Алания'),
('50', 'Вологодская область'),
('51', 'Новосибирская область'),
('52', 'Республика Адыгея'),
('53', 'Белгородская область'),
('54', 'Новгородская область'),
('55', 'Волгоградская область'),
('56', 'Республика Калмыкия'),
('57', 'Приморский край'),
('58', 'Самарская область'),
('59', 'Алтайский край'),
('60', 'Магаданская область'),
('61', 'Удмуртская Республика'),
('62', 'Челябинская область'),
('63', 'Иркутская область'),
('64', 'Костромская область'),
('65', 'Карачаево-Черкесская Республика'),
('66', 'Республика Саха (Якутия)'),
('67', 'Республика Хакасия'),
('68', 'Республика Крым'),
('69', 'Псковская область'),
('70', 'Архангельская область'),
('71', 'Забайкальский край'),
('72', 'Омская область'),
('73', 'Камчатский край'),
('74', 'Кемеровская область'),
('75', 'Томская область'),
('76', 'Еврейская автономная область'),
('77', 'Сахалинская область'),
('78', 'Республика Карелия'),
('79', 'Амурская область'),
('80', 'Курганская область'),
('81', 'Севастополь'),
('82', 'Республика Тыва'),
('83', 'Республика Алтай'),
('84', 'Чукотский автономный округ'),
('85', 'Ненецкий автономный округ');
insert into covid.cvd_updates(created_on, datetime)
SELECT u.created_on, u.datetime from public.update_info u;
insert into covid.cvd_stats(region_id, created_on, sick, healed, died, update_id)
select r.id,
s.created_on,
s.sick,
s.healed,
s.died,
u_new.id
from public.covid_statistics as s
inner join covid.regions as r
on s.region_id = r.name
inner join public.update_info as u_old
on u_old.id = s.update_info_id
inner join covid.cvd_updates as u_new
on u_new.datetime = u_old.datetime;

View File

@ -6,10 +6,15 @@ dependencies {
compile(project(':covid19-model'))
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.apache.camel.springboot:camel-spring-boot-starter:3.1.0'
implementation "org.apache.camel.springboot:camel-spring-boot-starter:${camelVersion}"
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.jsoup:jsoup:1.13.1'
compileOnly "org.springframework.boot:spring-boot-configuration-processor:${springBootVersion}"
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor:${springBootVersion}"
implementation "org.jsoup:jsoup:${jsoupVersion}"
implementation "org.apache.camel:camel-mail:${camelVersion}"
runtimeOnly 'org.postgresql:postgresql'

View File

@ -17,8 +17,10 @@ limitations under the License.
package com.bvn13.covid19.scheduler;
import com.bvn13.covid19.model.Covid19ModelConfig;
import com.bvn13.covid19.scheduler.updater.stopcoronovirusrf.MailConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@ -27,6 +29,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@Import({
Covid19ModelConfig.class
})
@EnableConfigurationProperties(MailConfig.class)
public class Covid19SchedulerApplication {
public static void main(String[] args) {

View File

@ -16,10 +16,10 @@ limitations under the License.
package com.bvn13.covid19.scheduler;
import com.bvn13.covid19.model.entities.CovidUpdateInfo;
import com.bvn13.covid19.model.entities.CovidUpdate;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CovidUpdateInfosRepository extends JpaRepository<CovidUpdateInfo, Long> {
public interface CovidUpdateInfosRepository extends JpaRepository<CovidUpdate, Long> {
}

View File

@ -0,0 +1,53 @@
/*
Copyright [2020] [bvn13]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.bvn13.covid19.scheduler.updater.stopcoronovirusrf;
import org.springframework.boot.context.properties.ConfigurationProperties;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
@ConfigurationProperties(prefix = "app.mail")
public class MailConfig {
@NotBlank
private String username;
@NotBlank
private String password;
@NotBlank
private String host;
@Min(1)
@Max(65536)
private int port;
@NotBlank
private String sender;
@NotBlank
private String recipient;
@NotBlank
private String subject;
public String constructEndpoint() {
return "smtp://" + host + ":" + port +
"?username=" + username +
"&password=" + password +
"&from=" + sender +
"&to=" + recipient +
"&subject=" + subject;
}
}

View File

@ -17,11 +17,11 @@ limitations under the License.
package com.bvn13.covid19.scheduler.updater.stopcoronovirusrf;
import com.bvn13.covid19.model.entities.CovidStat;
import com.bvn13.covid19.model.entities.CovidUpdate;
import com.bvn13.covid19.model.entities.Region;
import com.bvn13.covid19.model.entities.CovidUpdateInfo;
import com.bvn13.covid19.scheduler.CovidStatsRepository;
import com.bvn13.covid19.scheduler.RegionsRepository;
import com.bvn13.covid19.scheduler.CovidUpdateInfosRepository;
import com.bvn13.covid19.scheduler.RegionsRepository;
import com.bvn13.covid19.scheduler.updater.stopcoronovirusrf.model.RowData;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
@ -33,7 +33,6 @@ import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -59,31 +58,10 @@ public class StopcoronovirusRfService {
@Transactional
public void saveRawData(String date, Collection<RowData> rawData) {
ZonedDateTime zdt = parseZonedDateTime(date);
CovidUpdateInfo updateInfo = new CovidUpdateInfo();
updateInfo.setCreatedOn(LocalDateTime.now());
updateInfo.setDatetime(zdt);
updateInfo = updatesRepository.save(updateInfo);
Iterator<RowData> iter = rawData.iterator();
while (iter.hasNext()) {
RowData row = iter.next();
Region region = regionsRepository.findByName(row.getRegion()).orElseGet(() -> {
Region r = new Region();
r.setName(row.getRegion());
return regionsRepository.save(r);
});
CovidStat covidStat = new CovidStat();
covidStat.setUpdateInfo(updateInfo);
covidStat.setRegion(region);
covidStat.setSick(Long.parseLong(row.getSick()));
covidStat.setHealed(Long.parseLong(row.getHealed()));
covidStat.setDied(Long.parseLong(row.getDied()));
repository.save(covidStat);
ZonedDateTime dateOfData = parseZonedDateTime(date);
CovidUpdate updateInfo = createUpdateInfo(dateOfData);
for (RowData row : rawData) {
repository.save(createStats(row, updateInfo));
}
}
@ -104,6 +82,31 @@ public class StopcoronovirusRfService {
}
}
private CovidUpdate createUpdateInfo(ZonedDateTime dateOfData) {
CovidUpdate updateInfo = new CovidUpdate();
updateInfo.setCreatedOn(LocalDateTime.now());
updateInfo.setDatetime(dateOfData);
return updatesRepository.save(updateInfo);
}
private Region detectRegion(String region) {
return regionsRepository.findByName(region).orElseGet(() -> {
Region r = new Region();
r.setName(region);
return regionsRepository.save(r);
});
}
private CovidStat createStats(RowData row, CovidUpdate updateInfo) {
CovidStat covidStat = new CovidStat();
covidStat.setUpdateInfo(updateInfo);
covidStat.setRegion(detectRegion(row.getRegion()));
covidStat.setSick(Long.parseLong(row.getSick()));
covidStat.setHealed(Long.parseLong(row.getHealed()));
covidStat.setDied(Long.parseLong(row.getDied()));
return covidStat;
}
private int detectMonth(String m) {
switch (m.trim().toLowerCase()) {
case "январь":

View File

@ -19,7 +19,10 @@ package com.bvn13.covid19.scheduler.updater.stopcoronovirusrf;
import com.bvn13.covid19.scheduler.updater.stopcoronovirusrf.model.RowData;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.camel.*;
import org.apache.camel.Body;
import org.apache.camel.Handler;
import org.apache.camel.Header;
import org.apache.camel.LoggingLevel;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@ -36,12 +39,12 @@ public class StopcoronovirusRfUpdater extends RouteBuilder {
private final StopcoronovirusRfDataRetriever dataRetriever;
private final StopcoronovirusRfService service;
private final MailConfig mailConfig;
@Value("${app.timer.stopcoronovirusrf}")
private int stopcoronovirusrfTimerSecons;
private long stopcoronovirusrfTimerMillis;
@PostConstruct
public void init() {
stopcoronovirusrfTimerMillis = stopcoronovirusrfTimerSecons * 1000;
@ -50,6 +53,9 @@ public class StopcoronovirusRfUpdater extends RouteBuilder {
@Override
public void configure() throws Exception {
onException(RuntimeException.class)
.to(mailConfig.constructEndpoint());
from("timer:stopcoronovirusrf?delay=1000&period="+stopcoronovirusrfTimerMillis)
.log(LoggingLevel.DEBUG, log, "Start retrieving data from stopcoronovirus.rf")
.process(dataRetriever::retrieveData)

View File

@ -1,3 +1,6 @@
server:
port: 8081
app:
user-agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:74.0) Gecko/20100101 Firefox/74.0
zone-id: Europe/Moscow
@ -5,11 +8,21 @@ app:
# IN SECONDS
# 1 hour
stopcoronovirusrf: 3600
mail:
username: <mail-username>
password: <mail-password>
host: smtp.yandex.ru
port: 465
to: <your-password>
subject: Ошибка загрузки данных COVID19
spring:
application:
name: covid19-scheduler
flyway:
locations: classpath:db/migration
schemas: covid
datasource:
driver-class-name: org.postgresql.Driver
@ -21,10 +34,11 @@ spring:
show-sql: true
hibernate:
dialect: org.hibernate.dialect.PostgreSQL
ddl-auto: update
ddl-auto: validate
properties:
hibernate:
default_schema: covid
use_sql_comments: true
format_sql: true
show_sql: true

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists