Compare commits

...

10 Commits

Author SHA1 Message Date
dependabot[bot]
7889bf48da Bump org.springframework.cloud:spring-cloud-dependencies
Some checks failed
deploy to hetzner / deploy (push) Failing after 7m27s
Bumps [org.springframework.cloud:spring-cloud-dependencies](https://github.com/spring-cloud/spring-cloud-release) from 2023.0.3 to 2024.0.0.
- [Release notes](https://github.com/spring-cloud/spring-cloud-release/releases)
- [Commits](https://github.com/spring-cloud/spring-cloud-release/compare/v2023.0.3...v2024.0.0)

---
updated-dependencies:
- dependency-name: org.springframework.cloud:spring-cloud-dependencies
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 00:06:31 +01:00
dependabot[bot]
ab230ef715 Bump org.springframework.boot:spring-boot-starter-parent
Bumps [org.springframework.boot:spring-boot-starter-parent](https://github.com/spring-projects/spring-boot) from 3.3.5 to 3.4.0.
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.5...v3.4.0)

---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-parent
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 00:06:18 +01:00
dependabot[bot]
dfe260f412 Bump org.springdoc:springdoc-openapi-starter-webmvc-ui
Bumps [org.springdoc:springdoc-openapi-starter-webmvc-ui](https://github.com/springdoc/springdoc-openapi) from 2.6.0 to 2.7.0.
- [Release notes](https://github.com/springdoc/springdoc-openapi/releases)
- [Changelog](https://github.com/springdoc/springdoc-openapi/blob/main/CHANGELOG.md)
- [Commits](https://github.com/springdoc/springdoc-openapi/compare/v2.6.0...v2.7.0)

---
updated-dependencies:
- dependency-name: org.springdoc:springdoc-openapi-starter-webmvc-ui
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 00:04:28 +01:00
dependabot[bot]
577c97f1bb Bump org.springframework.modulith:spring-modulith-bom
Bumps [org.springframework.modulith:spring-modulith-bom](https://github.com/spring-projects/spring-modulith) from 1.2.5 to 1.3.0.
- [Release notes](https://github.com/spring-projects/spring-modulith/releases)
- [Commits](https://github.com/spring-projects/spring-modulith/compare/1.2.5...1.3.0)

---
updated-dependencies:
- dependency-name: org.springframework.modulith:spring-modulith-bom
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 00:02:56 +01:00
ee1ab34631 Revert "fixing opentelemetry uri"
This reverts commit 2dae9c9e14.
2024-11-01 22:54:22 +01:00
2dae9c9e14 fixing opentelemetry uri
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2024-11-01 22:51:36 +01:00
8000a26aad reactivate testing
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2024-10-30 19:22:22 +01:00
3c2eea0eb5 rename main package name
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2024-10-30 12:09:51 +01:00
a184f4a038 remove hal+json and add pageable
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2024-10-30 12:02:30 +01:00
46dd36a846 more hal+json optimisation
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2024-10-29 09:42:30 +01:00
24 changed files with 134 additions and 103 deletions

View File

@@ -5,7 +5,7 @@
<env name="EUREKA" value="http://localhost:8761/eureka" /> <env name="EUREKA" value="http://localhost:8761/eureka" />
</envs> </envs>
<module name="timezone-service" /> <module name="timezone-service" />
<option name="SPRING_BOOT_MAIN_CLASS" value="dev.mars3142.fhq.timezone_service.Application" /> <option name="SPRING_BOOT_MAIN_CLASS" value="dev.mars3142.fhq.timezone.Application" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@@ -3,7 +3,7 @@
<module name="timezone-service" /> <module name="timezone-service" />
<extension name="coverage"> <extension name="coverage">
<pattern> <pattern>
<option name="PATTERN" value="dev.mars3142.fhq.timezone_service.*" /> <option name="PATTERN" value="dev.mars3142.fhq.timezone.*" />
<option name="ENABLED" value="true" /> <option name="ENABLED" value="true" />
</pattern> </pattern>
</extension> </extension>

16
pom.xml
View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version> <version>3.4.0</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>dev.mars3142.fhq</groupId> <groupId>dev.mars3142.fhq</groupId>
@@ -28,8 +28,8 @@
</scm> </scm>
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
<spring-modulith.version>1.2.5</spring-modulith.version> <spring-modulith.version>1.3.0</spring-modulith.version>
<spring-cloud.version>2023.0.3</spring-cloud.version> <spring-cloud.version>2024.0.0</spring-cloud.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -54,6 +54,10 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.modulith</groupId> <groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-test</artifactId> <artifactId>spring-modulith-starter-test</artifactId>
@@ -82,10 +86,6 @@
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.github.ben-manes.caffeine</groupId> <groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId> <artifactId>caffeine</artifactId>
@@ -94,7 +94,7 @@
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.6.0</version> <version>2.7.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>

View File

@@ -1,4 +1,4 @@
package dev.mars3142.fhq.timezone_service; package dev.mars3142.fhq.timezone;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@@ -1,4 +1,4 @@
package dev.mars3142.fhq.timezone_service.config; package dev.mars3142.fhq.timezone.config;
import java.time.Duration; import java.time.Duration;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;

View File

@@ -1,4 +1,4 @@
package dev.mars3142.fhq.timezone_service.config; package dev.mars3142.fhq.timezone.config;
import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.Info;

View File

@@ -1,4 +1,4 @@
package dev.mars3142.fhq.timezone_service.config; package dev.mars3142.fhq.timezone.config;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.CorsRegistry;

View File

@@ -1,4 +1,4 @@
package dev.mars3142.fhq.timezone_service.exceptions; package dev.mars3142.fhq.timezone.exceptions;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;

View File

@@ -1,17 +1,18 @@
package dev.mars3142.fhq.timezone_service.timezone.controllers; package dev.mars3142.fhq.timezone.timezone.controllers;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; import dev.mars3142.fhq.timezone.timezone.domain.model.response.TimezoneResponse;
import dev.mars3142.fhq.timezone.timezone.service.TimezoneService;
import dev.mars3142.fhq.timezone_service.timezone.domain.model.response.LocationResponse;
import dev.mars3142.fhq.timezone_service.timezone.domain.model.response.TimezoneResponse;
import dev.mars3142.fhq.timezone_service.timezone.service.TimezoneService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.val; import lombok.val;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -25,23 +26,28 @@ public class TimezoneController {
@GetMapping @GetMapping
@ResponseStatus(HttpStatus.OK) @ResponseStatus(HttpStatus.OK)
public TimezoneResponse getTimeZone( public TimezoneResponse getTimeZone(
@RequestHeader(value = "X-Forwarded-For", defaultValue = "127.0.0.1") String header) { @RequestHeader(value = "X-Forwarded-For", defaultValue = "127.0.0.1") String header_ip) {
val clientIp = header.split(",")[0]; val clientIp = header_ip.split(",")[0];
val ip = timeZoneService.getExternalIp(clientIp); val ip = timeZoneService.getExternalIp(clientIp);
val timezoneInfo = timeZoneService.getTimeZoneInfoByIp(ip); val timezoneInfo = timeZoneService.getTimeZoneInfoByIp(ip);
val posix = timeZoneService.getPosixTimeZone(timezoneInfo.timezone()); val posix = timeZoneService.getPosixTimeZone(timezoneInfo.timezone());
val response = new TimezoneResponse(); val response = new TimezoneResponse();
response.setTimezone(timezoneInfo.timezone()); response.setTimezone(timezoneInfo.timezone());
response.setPosix_tz(posix); response.setPosix_tz(posix);
response.add(linkTo(TimezoneController.class).slash(ip).withSelfRel());
return response; return response;
} }
@GetMapping("{area}") @GetMapping("{area}")
@ResponseStatus(HttpStatus.OK) @ResponseStatus(HttpStatus.OK)
public LocationResponse getLocations(@PathVariable String area) { public Page<String> getLocations(@PathVariable String area,
val locations = timeZoneService.getLocations(area); @RequestParam(value = "pageSize", required = false, defaultValue = "10") int pageSize,
return new LocationResponse(locations.size(), locations); @RequestParam(value = "page", required = false, defaultValue = "0") int page,
@RequestParam(value = "direction", required = false) String direction) {
var pageRequest = PageRequest.of(page, pageSize);
if (direction != null && !direction.isEmpty()) {
pageRequest = PageRequest.of(page, pageSize, Direction.fromString(direction), "location");
}
return timeZoneService.getPagedLocations(area, pageRequest);
} }
@GetMapping("{area}/{location}") @GetMapping("{area}/{location}")
@@ -52,7 +58,6 @@ public class TimezoneController {
val response = new TimezoneResponse(); val response = new TimezoneResponse();
response.setTimezone(timezone); response.setTimezone(timezone);
response.setPosix_tz(posix); response.setPosix_tz(posix);
response.add(linkTo(TimezoneController.class).slash(area).slash(location).withSelfRel());
return response; return response;
} }
} }

View File

@@ -1,4 +1,4 @@
package dev.mars3142.fhq.timezone_service.timezone.domain.entities.response; package dev.mars3142.fhq.timezone.timezone.domain.entities.response;
public record IPApiResponse(String status, String country, String countryCode, String region, String regionName, public record IPApiResponse(String status, String country, String countryCode, String region, String regionName,
String city, String zip, String lat, String lon, String timezone, String isp, String or, String city, String zip, String lat, String lon, String timezone, String isp, String or,

View File

@@ -0,0 +1,5 @@
package dev.mars3142.fhq.timezone.timezone.domain.entities.response;
public record IpifyResponse(String ip) {
}

View File

@@ -1,4 +1,4 @@
package dev.mars3142.fhq.timezone_service.timezone.domain.entities.response; package dev.mars3142.fhq.timezone.timezone.domain.entities.response;
public record TimeApiTimezoneZoneResponse(Interval dstInterval) { public record TimeApiTimezoneZoneResponse(Interval dstInterval) {

View File

@@ -0,0 +1,12 @@
package dev.mars3142.fhq.timezone.timezone.domain.model.response;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class TimezoneResponse {
private String timezone;
private String posix_tz;
}

View File

@@ -0,0 +1,19 @@
package dev.mars3142.fhq.timezone.timezone.service;
import dev.mars3142.fhq.timezone.timezone.domain.entities.response.IPApiResponse;
import dev.mars3142.fhq.timezone.timezone.domain.entities.response.TimeApiTimezoneZoneResponse;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
public interface TimezoneService {
String getExternalIp(String ip);
IPApiResponse getTimeZoneInfoByIp(String ip);
TimeApiTimezoneZoneResponse getTimeZoneInfo(String timezone);
String getPosixTimeZone(String timezone);
Page<String> getPagedLocations(String area, PageRequest pageRequest);
}

View File

@@ -1,10 +1,10 @@
package dev.mars3142.fhq.timezone_service.timezone.service.impl; package dev.mars3142.fhq.timezone.timezone.service.impl;
import dev.mars3142.fhq.timezone_service.exceptions.NotFoundException; import dev.mars3142.fhq.timezone.exceptions.NotFoundException;
import dev.mars3142.fhq.timezone_service.timezone.domain.entities.response.IPApiResponse; import dev.mars3142.fhq.timezone.timezone.domain.entities.response.IPApiResponse;
import dev.mars3142.fhq.timezone_service.timezone.domain.entities.response.IpifyResponse; import dev.mars3142.fhq.timezone.timezone.domain.entities.response.IpifyResponse;
import dev.mars3142.fhq.timezone_service.timezone.domain.entities.response.TimeApiTimezoneZoneResponse; import dev.mars3142.fhq.timezone.timezone.domain.entities.response.TimeApiTimezoneZoneResponse;
import dev.mars3142.fhq.timezone_service.timezone.service.TimezoneService; import dev.mars3142.fhq.timezone.timezone.service.TimezoneService;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@@ -15,6 +15,9 @@ import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import lombok.val; import lombok.val;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpStatusCode; import org.springframework.http.HttpStatusCode;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient;
@@ -48,18 +51,18 @@ public class TimezoneServiceImpl implements TimezoneService {
@Override @Override
@Cacheable(value = "TZInfoByIp", key = "{#ip}") @Cacheable(value = "TZInfoByIp", key = "{#ip}")
public IPApiResponse getTimeZoneInfoByIp(String ip) { public IPApiResponse getTimeZoneInfoByIp(String ip) {
return restClient return restClient
.get() .get()
.uri(builder -> builder .uri(builder -> builder
.scheme("http") .scheme("http")
.host("ip-api.com") .host("ip-api.com")
.path("json/" + ip) .path("json/" + ip)
.build()) .build())
.retrieve() .retrieve()
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> { .onStatus(HttpStatusCode::is4xxClientError, (request, response) -> {
throw new NotFoundException(); throw new NotFoundException();
}) })
.body(IPApiResponse.class); .body(IPApiResponse.class);
} }
@Override @Override
@@ -97,19 +100,41 @@ public class TimezoneServiceImpl implements TimezoneService {
} }
@Override @Override
@Cacheable(value = "locations", key = "{#area}") @Cacheable(value = "locations", key = "{#area, #pageRequest}")
public List<String> getLocations(String area) { public Page<String> getPagedLocations(String area, PageRequest pageRequest) {
val directory = new File("/usr/share/zoneinfo/" + area); val directory = new File("/usr/share/zoneinfo/" + area);
if (!directory.exists()) { if (!directory.exists()) {
throw new NotFoundException(); throw new NotFoundException();
} }
return Stream.of(Objects.requireNonNull(directory.listFiles())) return toPage(Stream.of(Objects.requireNonNull(directory.listFiles()))
.filter(file -> !file.isDirectory()) .filter(file -> !file.isDirectory())
.map(file -> { .map(file -> {
val path = file.getPath().split("/"); val path = file.getPath().split("/");
return path[path.length - 2] + "/" + path[path.length - 1]; return path[path.length - 2] + "/" + path[path.length - 1];
}) })
.sorted() .toList(), pageRequest);
.toList(); }
Page<String> toPage(List<String> list, PageRequest pageRequest) {
val pageSize = pageRequest.getPageSize();
val pageNumber = pageRequest.getPageNumber();
val totalPages = list.size() / pageSize;
int max = pageNumber >= totalPages ? list.size() : pageSize * (pageNumber + 1);
int min = pageNumber > totalPages ? max : pageSize * pageNumber;
var content = list.stream().sorted((left, right) -> {
if (!pageRequest.getSort().isSorted()) {
return 0;
}
if (Objects.requireNonNull(pageRequest.getSort().getOrderFor("location")).isAscending()) {
return left.compareTo(right);
} else {
return right.compareTo(left);
}
}
);
return new PageImpl<>(content.toList().subList(min, max), pageRequest, list.size());
} }
} }

View File

@@ -1,5 +0,0 @@
package dev.mars3142.fhq.timezone_service.timezone.domain.entities.response;
public record IpifyResponse(String ip) {
}

View File

@@ -1,7 +0,0 @@
package dev.mars3142.fhq.timezone_service.timezone.domain.model.response;
import java.util.List;
public record LocationResponse(int count, List<String> locations) {
}

View File

@@ -1,15 +0,0 @@
package dev.mars3142.fhq.timezone_service.timezone.domain.model.response;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.hateoas.RepresentationModel;
@Getter
@Setter
@RequiredArgsConstructor
public class TimezoneResponse extends RepresentationModel<TimezoneResponse> {
private String timezone;
private String posix_tz;
}

View File

@@ -1,18 +0,0 @@
package dev.mars3142.fhq.timezone_service.timezone.service;
import dev.mars3142.fhq.timezone_service.timezone.domain.entities.response.IPApiResponse;
import dev.mars3142.fhq.timezone_service.timezone.domain.entities.response.TimeApiTimezoneZoneResponse;
import java.util.List;
public interface TimezoneService {
String getExternalIp(String ip);
IPApiResponse getTimeZoneInfoByIp(String ip);
TimeApiTimezoneZoneResponse getTimeZoneInfo(String timezone);
String getPosixTimeZone(String timezone);
List<String> getLocations(String area);
}

View File

@@ -1,7 +1,7 @@
eureka: eureka:
client: client:
register-with-eureka: true
fetch-registry: true fetch-registry: true
register-with-eureka: true
service-url: service-url:
defaultZone: ${EUREKA} defaultZone: ${EUREKA}

View File

@@ -1,4 +1,4 @@
package dev.mars3142.fhq.timezone_service; package dev.mars3142.fhq.timezone;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;

View File

@@ -1,4 +1,4 @@
package dev.mars3142.fhq.timezone_service.timezone.service.impl; package dev.mars3142.fhq.timezone.timezone.service.impl;
import lombok.val; import lombok.val;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@@ -59,6 +59,6 @@ class TimezoneServiceImplTest {
} }
@Test @Test
void getLocations() { void getPagedLocations() {
} }
} }

View File

@@ -1,4 +1,4 @@
package dev.mars3142.fhq.timezone_service.timezone.web.controllers; package dev.mars3142.fhq.timezone.timezone.web.controllers;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;

View File

@@ -0,0 +1,10 @@
eureka:
client:
enabled: false
spring:
application:
name: timezone-service
cloud:
config:
enabled: false