diff --git a/libraries/pom.xml b/libraries/pom.xml index eaa2d9d38f..3627e74472 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -633,9 +633,26 @@ ${googleclient.version} - com.google.http-client - google-http-client-gson - ${googleclient.version} + com.google.http-client + google-http-client-gson + ${googleclient.version} + + + + + com.google.api-client + google-api-client + ${google-api.version} + + + com.google.oauth-client + google-oauth-client-jetty + ${google-api.version} + + + com.google.apis + google-api-services-sheets + ${google-sheets.version} @@ -710,5 +727,7 @@ 1.0.0 3.8.4 2.5.5 + 1.23.0 + v4-rev493-1.21.0 \ No newline at end of file diff --git a/libraries/src/main/java/com/baeldung/google/sheets/GoogleAuthorizeUtil.java b/libraries/src/main/java/com/baeldung/google/sheets/GoogleAuthorizeUtil.java new file mode 100644 index 0000000000..650a1d084c --- /dev/null +++ b/libraries/src/main/java/com/baeldung/google/sheets/GoogleAuthorizeUtil.java @@ -0,0 +1,42 @@ +package com.baeldung.google.sheets; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.security.GeneralSecurityException; +import java.util.Arrays; +import java.util.List; + +import com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; +import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; +import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.client.util.store.MemoryDataStoreFactory; +import com.google.api.services.sheets.v4.SheetsScopes; + +public class GoogleAuthorizeUtil { + public static Credential authorize() throws IOException, GeneralSecurityException { + InputStream in = GoogleAuthorizeUtil.class.getResourceAsStream("/google-sheets-client-secret.json"); + GoogleClientSecrets clientSecrets = GoogleClientSecrets + .load(JacksonFactory.getDefaultInstance(), new InputStreamReader(in)); + + List scopes = Arrays.asList(SheetsScopes.SPREADSHEETS); + + GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow + .Builder(GoogleNetHttpTransport.newTrustedTransport(), + JacksonFactory.getDefaultInstance(), + clientSecrets, + scopes) + .setDataStoreFactory(new MemoryDataStoreFactory()) + .setAccessType("offline") + .build(); + Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()) + .authorize("user"); + + return credential; + } + +} diff --git a/libraries/src/main/java/com/baeldung/google/sheets/SheetsServiceUtil.java b/libraries/src/main/java/com/baeldung/google/sheets/SheetsServiceUtil.java new file mode 100644 index 0000000000..bbce96f389 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/google/sheets/SheetsServiceUtil.java @@ -0,0 +1,23 @@ +package com.baeldung.google.sheets; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +import com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.services.sheets.v4.Sheets; + +public class SheetsServiceUtil { + + private static final String APPLICATION_NAME = "Google Sheets Example"; + + public static Sheets getSheetsService() throws IOException, GeneralSecurityException { + Credential credential = GoogleAuthorizeUtil.authorize(); + return new Sheets.Builder(GoogleNetHttpTransport.newTrustedTransport(), + JacksonFactory.getDefaultInstance(), credential) + .setApplicationName(APPLICATION_NAME) + .build(); + } + +} diff --git a/libraries/src/main/resources/google-sheets-client-secret.json b/libraries/src/main/resources/google-sheets-client-secret.json new file mode 100644 index 0000000000..c92ccd6b9b --- /dev/null +++ b/libraries/src/main/resources/google-sheets-client-secret.json @@ -0,0 +1 @@ +{"installed":{"client_id":"394827218507-2ev02b2ha8plt7g2lh5nqse02ee737cf.apps.googleusercontent.com","project_id":"decisive-octane-187810","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://accounts.google.com/o/oauth2/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"2MnN1DfenoCGWMay3v8Bf7eI","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}} \ No newline at end of file diff --git a/libraries/src/test/java/com/baeldung/google/sheets/GoogleSheetsIntegrationTest.java b/libraries/src/test/java/com/baeldung/google/sheets/GoogleSheetsIntegrationTest.java new file mode 100644 index 0000000000..5280073be2 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/google/sheets/GoogleSheetsIntegrationTest.java @@ -0,0 +1,140 @@ +package com.baeldung.google.sheets; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.BeforeClass; +import org.junit.Test; + +import com.google.api.services.sheets.v4.Sheets; +import com.google.api.services.sheets.v4.model.AppendValuesResponse; +import com.google.api.services.sheets.v4.model.BatchGetValuesResponse; +import com.google.api.services.sheets.v4.model.BatchUpdateSpreadsheetRequest; +import com.google.api.services.sheets.v4.model.BatchUpdateValuesRequest; +import com.google.api.services.sheets.v4.model.BatchUpdateValuesResponse; +import com.google.api.services.sheets.v4.model.CopyPasteRequest; +import com.google.api.services.sheets.v4.model.GridRange; +import com.google.api.services.sheets.v4.model.Request; +import com.google.api.services.sheets.v4.model.Spreadsheet; +import com.google.api.services.sheets.v4.model.SpreadsheetProperties; +import com.google.api.services.sheets.v4.model.UpdateSpreadsheetPropertiesRequest; +import com.google.api.services.sheets.v4.model.UpdateValuesResponse; +import com.google.api.services.sheets.v4.model.ValueRange; + +import static org.assertj.core.api.Assertions.*; + +public class GoogleSheetsIntegrationTest { + + private static Sheets sheetsService; + + // this id can be replaced with your spreadsheet id + // otherwise be advised that multiple people may run this test and update the public spreadsheet + private static final String SPREADSHEET_ID = "1sILuxZUnyl_7-MlNThjt765oWshN3Xs-PPLfqYe4DhI"; + + @BeforeClass + public static void setup() throws GeneralSecurityException, IOException { + sheetsService = SheetsServiceUtil.getSheetsService(); + } + + @Test + public void whenWriteSheet_thenReadSheetOk() throws IOException { + ValueRange body = new ValueRange() + .setValues(Arrays.asList( + Arrays.asList("Expenses January"), + Arrays.asList("books", "30"), + Arrays.asList("pens", "10"), + Arrays.asList("Expenses February"), + Arrays.asList("clothes", "20"), + Arrays.asList("shoes", "5"))); + UpdateValuesResponse result = sheetsService.spreadsheets().values() + .update(SPREADSHEET_ID, "A1", body) + .setValueInputOption("RAW") + .execute(); + + List data = new ArrayList<>(); + data.add(new ValueRange() + .setRange("D1") + .setValues(Arrays.asList( + Arrays.asList("January Total", "=B2+B3")))); + data.add(new ValueRange() + .setRange("D4") + .setValues(Arrays.asList( + Arrays.asList("February Total", "=B5+B6")))); + + BatchUpdateValuesRequest batchBody = new BatchUpdateValuesRequest() + .setValueInputOption("USER_ENTERED") + .setData(data); + BatchUpdateValuesResponse batchResult = + sheetsService.spreadsheets().values() + .batchUpdate(SPREADSHEET_ID, batchBody) + .execute(); + + List ranges = Arrays.asList("E1","E4"); + BatchGetValuesResponse readResult = + sheetsService.spreadsheets().values() + .batchGet(SPREADSHEET_ID) + .setRanges(ranges) + .execute(); + + ValueRange januaryTotal = readResult.getValueRanges().get(0); + assertThat(januaryTotal.getValues().get(0).get(0)).isEqualTo("40"); + + ValueRange febTotal = readResult.getValueRanges().get(1); + assertThat(febTotal.getValues().get(0).get(0)).isEqualTo("25"); + + ValueRange appendBody = new ValueRange() + .setValues(Arrays.asList( + Arrays.asList("Total", "=E1+E4"))); + AppendValuesResponse appendResult = + sheetsService.spreadsheets().values() + .append(SPREADSHEET_ID, "A1", appendBody) + .setValueInputOption("USER_ENTERED") + .setInsertDataOption("INSERT_ROWS") + .setIncludeValuesInResponse(true) + .execute(); + + ValueRange total = appendResult.getUpdates().getUpdatedData(); + assertThat(total.getValues().get(0).get(1)).isEqualTo("65"); + } + + + @Test + public void whenUpdateSpreadSheetTitle_thenOk() throws IOException { + + UpdateSpreadsheetPropertiesRequest updateRequest = new UpdateSpreadsheetPropertiesRequest() + .setFields("*") + .setProperties(new SpreadsheetProperties().setTitle("Expenses")); + + CopyPasteRequest copyRequest = new CopyPasteRequest() + .setSource(new GridRange().setSheetId(0) + .setStartColumnIndex(0).setEndColumnIndex(2) + .setStartRowIndex(0).setEndRowIndex(1)) + .setDestination(new GridRange().setSheetId(1) + .setStartColumnIndex(0).setEndColumnIndex(2) + .setStartRowIndex(0).setEndRowIndex(1)) + .setPasteType("PASTE_VALUES"); + + List requests = new ArrayList<>(); + + requests.add(new Request().setCopyPaste(copyRequest)); + requests.add(new Request().setUpdateSpreadsheetProperties(updateRequest)); + + BatchUpdateSpreadsheetRequest body = + new BatchUpdateSpreadsheetRequest().setRequests(requests); + + sheetsService.spreadsheets().batchUpdate(SPREADSHEET_ID, body).execute(); + } + + @Test + public void whenCreateSpreadSheet_thenIdOk() throws IOException { + Spreadsheet spreadSheet = new Spreadsheet() + .setProperties(new SpreadsheetProperties().setTitle("My Spreadsheet")); + Spreadsheet result = sheetsService.spreadsheets().create(spreadSheet).execute(); + + assertThat(result.getSpreadsheetId()).isNotNull(); + } + +} diff --git a/spring-5-reactive-client/.gitignore b/spring-5-reactive-client/.gitignore new file mode 100644 index 0000000000..dec013dfa4 --- /dev/null +++ b/spring-5-reactive-client/.gitignore @@ -0,0 +1,12 @@ +#folders# +.idea +/target +/neoDb* +/data +/src/main/webapp/WEB-INF/classes +*/META-INF/* + +# Packaged files # +*.jar +*.war +*.ear \ No newline at end of file diff --git a/spring-5-reactive-client/README.md b/spring-5-reactive-client/README.md new file mode 100644 index 0000000000..400e343263 --- /dev/null +++ b/spring-5-reactive-client/README.md @@ -0,0 +1,15 @@ +## Spring REST Example Project + +### The Course +The "REST With Spring" Classes: http://bit.ly/restwithspring + +### Relevant Articles + +- [Concurrent Test Execution in Spring 5](http://www.baeldung.com/spring-5-concurrent-tests) +- [Introduction to the Functional Web Framework in Spring 5](http://www.baeldung.com/spring-5-functional-web) +- [Exploring the Spring 5 MVC URL Matching Improvements](http://www.baeldung.com/spring-5-mvc-url-matching) +- [Spring 5 WebClient](http://www.baeldung.com/spring-5-webclient) +- [Spring 5 Functional Bean Registration](http://www.baeldung.com/spring-5-functional-beans) +- [The SpringJUnitConfig and SpringJUnitWebConfig Annotations in Spring 5](http://www.baeldung.com/spring-5-junit-config) +- [Spring Security 5 for Reactive Applications](http://www.baeldung.com/spring-security-5-reactive) +- [Spring 5 Testing with @EnabledIf Annotation](https://github.com/eugenp/tutorials/tree/master/spring-5) diff --git a/spring-5-reactive-client/pom.xml b/spring-5-reactive-client/pom.xml new file mode 100644 index 0000000000..8aa579b724 --- /dev/null +++ b/spring-5-reactive-client/pom.xml @@ -0,0 +1,201 @@ + + + 4.0.0 + + com.baeldung + spring-5-reactive-client + 0.0.1-SNAPSHOT + jar + + spring-5 + spring 5 sample project about new features + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M7 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-webflux + + + org.projectreactor + reactor-spring + ${reactor-spring.version} + + + javax.json.bind + javax.json.bind-api + + + + + + + + + + + + + + + + + org.apache.geronimo.specs + geronimo-json_1.1_spec + ${geronimo-json_1.1_spec.version} + + + org.apache.johnzon + johnzon-jsonb + + + + org.apache.commons + commons-lang3 + + + + + + org.springframework.boot + spring-boot-devtools + runtime + + + com.h2database + h2 + runtime + + + + org.springframework + spring-test + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.apache.commons + commons-collections4 + 4.1 + test + + + + org.junit.jupiter + junit-jupiter-api + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + test + + + org.junit.platform + junit-platform-runner + ${junit.platform.version} + test + + + + org.projectlombok + lombok + + + org.apache.commons + commons-lang3 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + com.baeldung.Spring5Application + JAR + + + + + org.apache.maven.plugins + maven-surefire-plugin + + 3 + true + methods + true + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + UTF-8 + UTF-8 + 1.8 + 1.0.0 + 5.0.0 + 2.20 + 5.0.1.RELEASE + 1.0.1.RELEASE + 1.1.3 + 1.0 + 1.0 + + + diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/controller/Foo.java b/spring-5-reactive-client/src/main/java/com/baeldung/reactive/model/Foo.java similarity index 78% rename from spring-5-reactive/src/main/java/com/baeldung/reactive/controller/Foo.java rename to spring-5-reactive-client/src/main/java/com/baeldung/reactive/model/Foo.java index 480782a551..2c49e6146a 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/controller/Foo.java +++ b/spring-5-reactive-client/src/main/java/com/baeldung/reactive/model/Foo.java @@ -1,4 +1,4 @@ -package com.baeldung.reactive.controller; +package com.baeldung.reactive.model; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/spring-5-reactive-client/src/main/resources/application.properties b/spring-5-reactive-client/src/main/resources/application.properties new file mode 100644 index 0000000000..2d93456aeb --- /dev/null +++ b/spring-5-reactive-client/src/main/resources/application.properties @@ -0,0 +1,3 @@ +logging.level.root=INFO + +server.port=8081 \ No newline at end of file diff --git a/spring-5-reactive-client/src/main/resources/logback.xml b/spring-5-reactive-client/src/main/resources/logback.xml new file mode 100644 index 0000000000..8bbe8c1d67 --- /dev/null +++ b/spring-5-reactive-client/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + # Pattern of log message for console appender + %d{yyyy-MM-dd HH:mm:ss} %-5p %m%n + + + + + + + + + + \ No newline at end of file diff --git a/spring-5-reactive-client/src/main/webapp/WEB-INF/web.xml b/spring-5-reactive-client/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..bfcf43dad2 --- /dev/null +++ b/spring-5-reactive-client/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,21 @@ + + + + Spring Functional Application + + + functional + com.baeldung.functional.RootServlet + 1 + true + + + functional + / + + + + \ No newline at end of file diff --git a/spring-5-reactive-client/src/test/java/com/baeldung/reactive/ReactiveIntegrationTest.java b/spring-5-reactive-client/src/test/java/com/baeldung/reactive/ReactiveIntegrationTest.java new file mode 100644 index 0000000000..394ff42e5f --- /dev/null +++ b/spring-5-reactive-client/src/test/java/com/baeldung/reactive/ReactiveIntegrationTest.java @@ -0,0 +1,42 @@ +package com.baeldung.reactive; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.WebClient; + +import com.baeldung.reactive.model.Foo; + +import reactor.core.publisher.Mono; + +@SpringBootTest +public class ReactiveIntegrationTest { + + private WebClient client; + + @BeforeEach + public void before() { + client = WebClient.create("http://localhost:8080"); + } + + // + + @Test + public void whenMonoReactiveEndpointIsConsumed_thenCorrectOutput() { + final Mono fooMono = client.get().uri("/foos/123").exchange().log(); + + System.out.println(fooMono.subscribe()); + } + + @Test + public void whenFluxReactiveEndpointIsConsumed_thenCorrectOutput() throws InterruptedException { + client.get().uri("/foos") + .retrieve() + .bodyToFlux(Foo.class).log() + .subscribe(System.out::println); + + System.out.println(); + } + +} diff --git a/spring-5-reactive-client/src/test/java/com/baeldung/reactive/Spring5ReactiveTestApplication.java b/spring-5-reactive-client/src/test/java/com/baeldung/reactive/Spring5ReactiveTestApplication.java new file mode 100644 index 0000000000..c884ace323 --- /dev/null +++ b/spring-5-reactive-client/src/test/java/com/baeldung/reactive/Spring5ReactiveTestApplication.java @@ -0,0 +1,35 @@ +package com.baeldung.reactive; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.web.reactive.function.client.WebClient; + +import com.baeldung.reactive.model.Foo; + +@SpringBootApplication +public class Spring5ReactiveTestApplication { + + @Bean + public WebClient client() { + return WebClient.create("http://localhost:8080"); + } + + @Bean + CommandLineRunner cmd(WebClient client) { + return args -> { + client.get().uri("/foos2") + .retrieve() + .bodyToFlux(Foo.class).log() + .subscribe(System.out::println); + }; + } + + // + + public static void main(String[] args) { + SpringApplication.run(Spring5ReactiveTestApplication.class, args); + } + +} diff --git a/spring-5-reactive-client/src/test/resources/logback-test.xml b/spring-5-reactive-client/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..8bbe8c1d67 --- /dev/null +++ b/spring-5-reactive-client/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + # Pattern of log message for console appender + %d{yyyy-MM-dd HH:mm:ss} %-5p %m%n + + + + + + + + + + \ No newline at end of file diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/controller/FooReactiveController.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/controller/FooReactiveController.java index 1115036ad3..933d469f65 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/controller/FooReactiveController.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/controller/FooReactiveController.java @@ -3,13 +3,19 @@ package com.baeldung.reactive.controller; import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; import java.time.Duration; +import java.util.Random; +import java.util.stream.Stream; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; +import com.baeldung.reactive.model.Foo; + import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.util.function.Tuple2; @RestController public class FooReactiveController { @@ -19,11 +25,18 @@ public class FooReactiveController { return Mono.just(new Foo(id, randomAlphabetic(6))); } - @GetMapping("/foos") + @GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE, value = "/foos") + public Flux getAllFoos2() { + final Flux foosFlux = Flux.fromStream(Stream.generate(() -> new Foo(new Random().nextLong(), randomAlphabetic(6)))); + final Flux emmitFlux = Flux.interval(Duration.ofSeconds(1)); + return Flux.zip(foosFlux, emmitFlux).map(Tuple2::getT1); + } + + @GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE, value = "/foos2") public Flux getAllFoos() { final Flux flux = Flux. create(fluxSink -> { while (true) { - fluxSink.next(new Foo(System.currentTimeMillis(), randomAlphabetic(6))); + fluxSink.next(new Foo(new Random().nextLong(), randomAlphabetic(6))); } }).sample(Duration.ofSeconds(1)).log(); diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/model/Foo.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/model/Foo.java new file mode 100644 index 0000000000..2c49e6146a --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/model/Foo.java @@ -0,0 +1,13 @@ +package com.baeldung.reactive.model; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@AllArgsConstructor +@Data +public class Foo { + + private long id; + private String name; + +} diff --git a/spring-5-reactive/src/main/resources/files/hello.txt b/spring-5-reactive/src/main/resources/files/hello.txt deleted file mode 100644 index b6fc4c620b..0000000000 --- a/spring-5-reactive/src/main/resources/files/hello.txt +++ /dev/null @@ -1 +0,0 @@ -hello \ No newline at end of file diff --git a/spring-5-reactive/src/main/resources/files/test/test.txt b/spring-5-reactive/src/main/resources/files/test/test.txt deleted file mode 100644 index 30d74d2584..0000000000 --- a/spring-5-reactive/src/main/resources/files/test/test.txt +++ /dev/null @@ -1 +0,0 @@ -test \ No newline at end of file diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/FluxUnitTest.java b/spring-5-reactive/src/test/java/com/baeldung/reactive/FluxUnitTest.java index 5499e72877..bad5fc5f22 100644 --- a/spring-5-reactive/src/test/java/com/baeldung/reactive/FluxUnitTest.java +++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/FluxUnitTest.java @@ -4,10 +4,11 @@ import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; import static org.junit.Assert.assertNotNull; import java.time.Duration; +import java.util.Random; import org.junit.jupiter.api.Test; -import com.baeldung.reactive.controller.Foo; +import com.baeldung.reactive.model.Foo; import reactor.core.publisher.Flux; @@ -17,7 +18,7 @@ public class FluxUnitTest { public void whenFluxIsConstructed_thenCorrect() { final Flux flux = Flux. create(fluxSink -> { while (true) { - fluxSink.next(new Foo(System.currentTimeMillis(), randomAlphabetic(6))); + fluxSink.next(new Foo(new Random().nextLong(), randomAlphabetic(6))); } }).sample(Duration.ofSeconds(1)).log();