Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
47e0230
bump version
y-lakhdar Aug 26, 2022
16229dd
chore: update CODEOWNERS (#23)
louis-bompart Jan 11, 2023
9f6c8a3
Update README.md
olamothe Mar 23, 2023
f3889db
chore: create dependency-review.yml (#24)
JPLachance Apr 21, 2023
4731e1b
ci: add codeql workflow (#26)
y-lakhdar May 19, 2023
3a6b08a
feat: create `PushSource` and `catalogSource` classes (#30)
y-lakhdar May 25, 2023
d957202
chore: use CodeQL public action (#31)
y-lakhdar May 26, 2023
071ba14
feat: implement `StreamService` class (#32)
y-lakhdar May 31, 2023
cd32e2e
fix: provide JSON value (#36)
y-lakhdar Jun 1, 2023
cce3baf
feat: implement `DocumentUploadQueue` class (#33)
y-lakhdar Jun 1, 2023
aa3335c
feat: add create method in `CatalogSource` and `PushSource` (LENS-874…
hdhayneCoveo Jun 2, 2023
0158c99
fix: bug related to the type expected of delete documents
hdhayneCoveo Jun 8, 2023
ea3153c
docs: create Sample Code classes to create push and Catalog Source in…
hdhayneCoveo Jun 8, 2023
2631e74
feat: implement `PushService` class (#34)
y-lakhdar Jun 9, 2023
d9b0b47
ci: ensure semantic title (#41)
y-lakhdar Jun 9, 2023
8a0f6b6
ci: format code base (#44)
y-lakhdar Jun 9, 2023
bc2af2e
fix: in StreamService streamId was never set , with this PR , it is r…
hdhayneCoveo Jun 9, 2023
f8e25da
ci: fix typo in GH workflow (#45)
y-lakhdar Jun 12, 2023
a21ccb8
ci: automate package publish (#47)
y-lakhdar Jun 12, 2023
7e90aa4
ci: adjust script path (#49)
y-lakhdar Jun 12, 2023
fea6a26
ci: add missing environment variables (#50)
y-lakhdar Jun 12, 2023
77cbdd1
ci: add missing export (#51)
y-lakhdar Jun 12, 2023
1feb9cc
docs: create sample code to stream document (#43)
hdhayneCoveo Jun 12, 2023
d28fe2e
feat:expose publicly PushSource,CatalogSource,StreamService (#46)
hdhayneCoveo Jun 12, 2023
3456646
ci: fix release script (#52)
y-lakhdar Jun 13, 2023
52b3557
ci: configure Renovate (#56)
renovate[bot] Jun 13, 2023
6f343d2
chore(deps): update dependency junit:junit to v4.13.1 [security] (#58)
renovate[bot] Jun 13, 2023
366323d
fix(deps): update dependency com.google.code.gson:gson to v2.8.9 [sec…
renovate[bot] Jun 13, 2023
bb8d5fd
chore(deps): update actions/checkout digest to c85c95e (#60)
renovate[bot] Jun 13, 2023
d89982f
chore(deps): update dependency junit:junit to v4.13.2 (#61)
renovate[bot] Jun 13, 2023
c8559aa
chore(deps): update dependency @commitlint/config-conventional to v17…
renovate[bot] Jun 13, 2023
3ddf532
chore(deps): update dependency org.apache.maven.plugins:maven-javadoc…
renovate[bot] Jun 13, 2023
57dc725
ci: fix release script (#54)
y-lakhdar Jun 13, 2023
f757ac1
chore(main): release 2.0.1-SNAPSHOT (#53)
developer-experience-bot[bot] Jun 13, 2023
1609aca
chore(main): release 2.0.1-SNAPSHOT (#64)
developer-experience-bot[bot] Jun 13, 2023
9631b80
ci: update renovate config (#55)
y-lakhdar Jun 14, 2023
2989459
feat: add logger on queue
y-lakhdar Jun 15, 2023
1ee7017
Merge branch 'main' of github.com:coveo/push-api-client.java into LEN…
y-lakhdar Jun 20, 2023
8534496
chore: fix merge conflict
y-lakhdar Jun 20, 2023
3e88e22
feat: add logging
y-lakhdar Jun 22, 2023
1f00c87
Merge branch 'main' into LENS-912
y-lakhdar Jun 22, 2023
161e45b
remove extra log
y-lakhdar Jun 22, 2023
9579550
add logger to APICore
y-lakhdar Jun 26, 2023
ed64512
add unit tests
y-lakhdar Jun 26, 2023
b9a8f91
remove unecessary changes
y-lakhdar Jun 26, 2023
a656d38
revert formatting
y-lakhdar Jun 26, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,31 @@ public class PushOneDocument {

```

## Logging
When pushing multiple documents into your source using a service (e.g. `PushService`, `StreamService`), make sure to configure a **logger** to be able to see what happens.
to do so .. in your `resources` folder.

### Log4j2 XML Configuration Example
To log execution output into the console, use the below `log4j2.xml` configuration:
```xml
<!-- log4j2.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="ConsoleAppender" />
</Root>
</Loggers>
</Configuration>
```

See [Log4j2 configuration](https://logging.apache.org/log4j/2.x/manual/configuration.html) for more details.

## Local Setup to Contribute

### Formatting
Expand Down
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@
</profiles>

<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
Expand Down
49 changes: 44 additions & 5 deletions src/main/java/com/coveo/pushapiclient/ApiCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,22 @@
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublisher;
import java.net.http.HttpResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

// TODO: LENS-934 - Support throttling
class ApiCore {
private final HttpClient httpClient;
private final Logger logger;

public ApiCore() {
this.httpClient = HttpClient.newHttpClient();
this.logger = LogManager.getLogger(ApiCore.class);
}

public ApiCore(HttpClient httpClient) {
public ApiCore(HttpClient httpClient, Logger logger) {
this.httpClient = httpClient;
this.logger = logger;
}

public HttpResponse<String> post(URI uri, String[] headers)
Expand All @@ -26,26 +31,60 @@ public HttpResponse<String> post(URI uri, String[] headers)

public HttpResponse<String> post(URI uri, String[] headers, BodyPublisher body)
throws IOException, InterruptedException {
this.logger.debug("POST " + uri);
HttpRequest request = HttpRequest.newBuilder().headers(headers).uri(uri).POST(body).build();
return this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response =
this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
this.logResponse(response);
return response;
}

public HttpResponse<String> put(URI uri, String[] headers, BodyPublisher body)
throws IOException, InterruptedException {
this.logger.debug("PUT " + uri);
HttpRequest request = HttpRequest.newBuilder().headers(headers).uri(uri).PUT(body).build();
return this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response =
this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
this.logResponse(response);
return response;
}

public HttpResponse<String> delete(URI uri, String[] headers)
throws IOException, InterruptedException {
this.logger.debug("DELETE " + uri);
HttpRequest request = HttpRequest.newBuilder().headers(headers).uri(uri).DELETE().build();
return this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response =
this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
this.logResponse(response);
return response;
}

public HttpResponse<String> delete(URI uri, String[] headers, BodyPublisher body)
throws IOException, InterruptedException {
this.logger.debug("DELETE " + uri);
HttpRequest request =
HttpRequest.newBuilder().headers(headers).uri(uri).method("DELETE", body).build();
return this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response =
this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
this.logResponse(response);
return response;
}

private void logResponse(HttpResponse<String> response) {
if (response == null) {
return;
}
int status = response.statusCode();
String method = response.request().method();
String statusMessage = method + " status: " + status;
String responseMessage = method + " response: " + response.body();

if (status < 200 || status >= 300) {
this.logger.error(statusMessage);
this.logger.error(responseMessage);
} else {
this.logger.debug(statusMessage);
this.logger.debug(responseMessage);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import java.io.IOException;
import java.util.ArrayList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/** Represents a queue for uploading documents using a specified upload strategy */
class DocumentUploadQueue {
private static final Logger logger = LogManager.getLogger(DocumentUploadQueue.class);
private final UploadStrategy uploader;
private final int maxQueueSize = 5 * 1024 * 1024;
private ArrayList<DocumentBuilder> documentToAddList;
Expand All @@ -30,11 +33,14 @@ public DocumentUploadQueue(UploadStrategy uploader) {
*/
public void flush() throws IOException, InterruptedException {
if (this.isEmpty()) {
logger.debug("Empty batch. Skipping upload");
return;
}
BatchUpdate batch = this.getBatch();
// TODO: LENS-871: support concurrent requests
BatchUpdate batch = this.getBatch();
logger.info("Uploading document batch");
this.uploader.apply(batch);

this.size = 0;
this.documentToAddList.clear();
this.documentToDeleteList.clear();
Expand All @@ -58,6 +64,7 @@ public void add(DocumentBuilder document) throws IOException, InterruptedExcepti
this.flush();
}
documentToAddList.add(document);
logger.info("Adding document to batch: " + document.getDocument().uri);
this.size += sizeOfDoc;
}

Expand All @@ -79,6 +86,7 @@ public void add(DeleteDocument document) throws IOException, InterruptedExceptio
this.flush();
}
documentToDeleteList.add(document);
logger.info("Adding document to batch: " + document.documentId);
this.size += sizeOfDoc;
}

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/coveo/pushapiclient/PlatformClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.stream.Stream;
import org.apache.logging.log4j.LogManager;

/** PlatformClient handles network requests to the Coveo platform */
public class PlatformClient {
Expand Down Expand Up @@ -58,7 +59,7 @@ public PlatformClient(String apiKey, String organizationId, PlatformUrl platform
public PlatformClient(String apiKey, String organizationId, HttpClient httpClient) {
this.apiKey = apiKey;
this.organizationId = organizationId;
this.api = new ApiCore(httpClient);
this.api = new ApiCore(httpClient, LogManager.getLogger(ApiCore.class));
this.platformUrl = new PlatformUrlBuilder().build();
}

Expand Down
6 changes: 5 additions & 1 deletion src/main/java/com/coveo/pushapiclient/StreamService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.google.gson.Gson;
import java.io.IOException;
import java.net.http.HttpResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class StreamService {
private final StreamEnabledSource source;
Expand All @@ -27,11 +29,13 @@ public StreamService(StreamEnabledSource source) {
String organizationId = source.getOrganizationId();
PlatformUrl platformUrl = source.getPlatformUrl();
UploadStrategy uploader = this.getUploadStrategy();
Logger logger = LogManager.getLogger(StreamService.class);

this.source = source;
this.queue = new DocumentUploadQueue(uploader);
this.platformClient = new PlatformClient(apiKey, organizationId, platformUrl);
this.service = new StreamServiceInternal(this.source, this.queue, this.platformClient);

this.service = new StreamServiceInternal(this.source, this.queue, this.platformClient, logger);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@
import com.google.gson.Gson;
import java.io.IOException;
import java.net.http.HttpResponse;
import org.apache.logging.log4j.Logger;

/** For internal use only. Made to easily test the service without having to use PowerMock */
class StreamServiceInternal {
private Logger logger;
private final StreamEnabledSource source;
private final PlatformClient platformClient;
private String streamId;
private DocumentUploadQueue queue;

public StreamServiceInternal(
StreamEnabledSource source, DocumentUploadQueue queue, PlatformClient platformClient) {
StreamEnabledSource source,
DocumentUploadQueue queue,
PlatformClient platformClient,
Logger logger) {
this.source = source;
this.queue = queue;
this.platformClient = platformClient;
this.logger = logger;
}

public String add(DocumentBuilder document) throws IOException, InterruptedException {
Expand All @@ -35,10 +41,12 @@ public HttpResponse<String> close()
}
queue.flush();
String sourceId = this.getSourceId();
this.logger.info("Closing open stream " + this.streamId);
return this.platformClient.closeStream(sourceId, this.streamId);
}

private String getStreamId() throws IOException, InterruptedException {
this.logger.info("Opening new stream");
String sourceId = this.getSourceId();
HttpResponse<String> response = this.platformClient.openStream(sourceId);
StreamResponse streamResponse = new Gson().fromJson(response.body(), StreamResponse.class);
Expand Down
82 changes: 82 additions & 0 deletions src/test/java/com/coveo/pushapiclient/ApiCoreTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.coveo.pushapiclient;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandler;
import org.apache.logging.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class ApiCoreTest {

@Mock private HttpClient httpClient;
@Mock private HttpRequest httpRequest;
@Mock private Logger logger;
@Mock private HttpResponse<String> httpResponse;

@InjectMocks private ApiCore api;

private AutoCloseable closeable;
private static final String[] headers = {
"Content-Type", "application/json", "Accept", "application/json"
};

private void mockSuccessResponse() {
when(httpResponse.statusCode()).thenReturn(200);
when(httpResponse.body()).thenReturn("All good!");
when(httpRequest.method()).thenReturn("POST");
}

private void mockErrorResponse() {
when(httpResponse.statusCode()).thenReturn(412);
when(httpResponse.body()).thenReturn("BAD_REQUEST");
when(httpRequest.method()).thenReturn("DELETE");
}

@Before
public void setUp() throws Exception {
closeable = MockitoAnnotations.openMocks(this);

when(httpClient.send(any(HttpRequest.class), any(BodyHandler.class))).thenReturn(httpResponse);
when(httpResponse.request()).thenReturn(httpRequest);
}

@After
public void closeService() throws Exception {
closeable.close();
}

@Test
public void testShouldLogRequestAndResonse()
throws IOException, InterruptedException, URISyntaxException {
this.mockSuccessResponse();
this.api.post(new URI("https://perdu.com/"), headers);

verify(logger, times(1)).debug("POST https://perdu.com/");
verify(logger, times(1)).debug("POST status: 200");
verify(logger, times(1)).debug("POST response: All good!");
}

@Test
public void testShouldLogResponse() throws IOException, InterruptedException, URISyntaxException {
this.mockErrorResponse();
this.api.delete(new URI("https://perdu.com/"), headers);

verify(logger, times(1)).debug("DELETE https://perdu.com/");
verify(logger, times(1)).error("DELETE status: 412");
verify(logger, times(1)).error("DELETE response: BAD_REQUEST");
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.coveo.pushapiclient;

import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.coveo.pushapiclient.exceptions.NoOpenStreamException;
import java.io.IOException;
import java.net.http.HttpResponse;
import org.apache.logging.log4j.core.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
Expand All @@ -21,6 +23,8 @@ public class StreamServiceInternalTest {

@Mock private PlatformClient platformClient;

@Mock private Logger logger;

@InjectMocks private StreamServiceInternal service;

@Mock private HttpResponse<String> httpResponse;
Expand Down Expand Up @@ -86,4 +90,14 @@ public void givenNoOpenStream_whenClose_thenShouldThrow()
throws IOException, InterruptedException, NoOpenStreamException {
service.close();
}

@Test
public void testShouldLogInfo() throws IOException, InterruptedException, NoOpenStreamException {
service.add(documentA);
service.add(documentB);
verify(logger, times(1)).info("Opening new stream");

service.close();
verify(logger, times(1)).info(contains("Closing open stream"));
}
}