Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding other status #4091

Merged
merged 4 commits into from
Jun 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public final class PollResponse<T> {
private final T value;
private final Duration retryAfter;
private final Map<Object, Object> properties;
private final String otherStatus;

/**
* An enum to represent all possible states that a long-running operation may find itself in.
Expand All @@ -49,7 +50,14 @@ public enum OperationStatus {

/** Represents that this long-running operation is cancelled by user, however this is still
* considered as complete long-running operation.*/
USER_CANCELLED
USER_CANCELLED,

/**
* When long-running operation status could not be represented by any status in {@link OperationStatus}, this status represents
* a custom status Azure service could be in. This custom status is not considered as complete long-running operation.
* It must have valid value for {@code otherStatus} as {@link String}.
*/
OTHER
}

/**
Expand All @@ -66,11 +74,27 @@ public enum OperationStatus {
* @throws NullPointerException If {@code status} is {@code null}.
*/
public PollResponse(OperationStatus status, T value, Duration retryAfter, Map<Object, Object> properties) {
this(status, null, value, retryAfter, properties);
}

/*
* Creates a new {@link PollResponse} with status, value and retryAfter.
*
* @param status Mandatory operation status as defined in {@link OperationStatus}.
* @param otherStatus string representation of custom status. It must be not null and non empty. The status will be defaulted to {@link OperationStatus#OTHER}
* @param value The value as a result of poll operation. This can be any custom user-defined object. Null is also valid.
* @param retryAfter Represents the delay the service has requested until the next polling operation is performed.
* A {@code null}, zero or negative value will be taken to mean that the {@link Poller} should determine on its own when the next poll operation is to occur.
* @param properties A map of properties provided by the service that will be made available into the next poll operation.
* @throws NullPointerException If {@code status} is {@code null}.
*/
private PollResponse(OperationStatus status, String otherStatus, T value, Duration retryAfter, Map<Object, Object> properties) {
Objects.requireNonNull(status, "The status input parameter cannot be null.");
this.status = status;
this.value = value;
this.retryAfter = retryAfter;
this.properties = properties;
this.otherStatus = otherStatus;
}

/**
Expand Down Expand Up @@ -103,6 +127,49 @@ public PollResponse(OperationStatus status, T value) {
this(status, value, null);
}

/**
* Creates a new {@link PollResponse} with status and value.
*
*<p><strong>Code Sample Creating PollResponse Object</strong></p>
* {@codesnippet com.azure.core.util.polling.pollresponse.custom.status.retryAfter}
*
* @param otherStatus string representation of custom status. It must be not null and non empty. The status will be defaulted to {@link OperationStatus#OTHER}
* @param value The value as a result of poll operation. This can be any custom user-defined object. Null is also valid.
* @param retryAfter Represents the delay the service has requested until the next polling operation is performed.
* A {@code null}, zero or negative value will be taken to mean that the {@link Poller} should determine on its own when the next poll operation is to occur.
* @throws NullPointerException If {@code status} is {@code null}.
* @throws IllegalArgumentException if otherStatus is null or empty when status is {@link OperationStatus#OTHER}.
*/
public PollResponse(String otherStatus, T value, Duration retryAfter) {
this(OperationStatus.OTHER, otherStatus, value, retryAfter, null);
if (Objects.isNull(otherStatus) || otherStatus.trim().length() == 0) {
throw new IllegalArgumentException("The otherStatus can not be empty or null.");
}
}

/**
* Creates a new {@link PollResponse} with custom status and value.
*
*<p><strong>Code Sample Creating PollResponse Object</strong></p>
* {@codesnippet com.azure.core.util.polling.pollresponse.custom.status}
*
* @param otherStatus string representation of custom status. It must be not null and non empty. The status will be defaulted to {@link OperationStatus#OTHER}
* @param value The value as a result of poll operation. This can be any custom user-defined object. Null is also valid.
* @throws NullPointerException If {@code status} is {@code null}.
* @throws IllegalArgumentException if otherStatus is null or empty when status is {@link OperationStatus#OTHER}.
*/
public PollResponse(String otherStatus, T value) {
this(otherStatus, value, null);
}

/**
* Used to retrieve value for custom status string when status is {@link OperationStatus#OTHER}.
* @return custom other status string when status is {@link OperationStatus#OTHER}.
*/
public String getOtherStatus() {
return this.otherStatus;
}

/**
* Represents the status of the long-running operation at the time the last polling operation finished successfully.
* @return A {@link OperationStatus} representing the result of the poll operation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,33 @@

public final class PollResponseJavaDocCodeSnippets<T> {

/**
*
* @param otherStatus v
* @param value v
*/
public void initialise(String otherStatus, T value) {
// BEGIN: com.azure.core.util.polling.pollresponse.custom.status
// Lets say we want to crete poll response with status as IN_PROGRESS
PollResponse<String> inProgressPollResponse
= new PollResponse<>("CUSTOM_OTHER_STATUS", "my custom response");
// END: com.azure.core.util.polling.pollresponse.custom.status
}

/**
*
* @param otherStatus v
* @param value v
* @param retryAfterDuration v
*/
public void initialise(String otherStatus, T value, Duration retryAfterDuration) {
// BEGIN: com.azure.core.util.polling.pollresponse.custom.status.retryAfter
// Lets say we want to crete poll response with status as IN_PROGRESS
PollResponse<String> inProgressPollResponse
= new PollResponse<>("CUSTOM_OTHER_STATUS", "my custom response", Duration.ofMillis(5000));
// END: com.azure.core.util.polling.pollresponse.custom.status.retryAfter
}

/**
* initialise
* @param status v
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@

import org.junit.Assert;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import static org.junit.Assert.assertTrue;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.function.Function;

public class PollerTests {
Expand Down Expand Up @@ -46,6 +49,102 @@ public Mono<PollResponse<CreateCertificateResponse>> apply(PollResponse<CreateCe
};
}

private Function<PollResponse<CreateCertificateResponse>, Mono<PollResponse<CreateCertificateResponse>>> createPollOperation(

final List<PollResponse<CreateCertificateResponse>> intermediateOtherPollResponseList,
final PollResponse<CreateCertificateResponse> finalPollResponse,
long sendFinalResponseInMillis
) {
return new Function<PollResponse<CreateCertificateResponse>, Mono<PollResponse<CreateCertificateResponse>>>() {
// Will return success after this time.
LocalDateTime timeToReturnFinalResponse = LocalDateTime.now().plus(Duration.ofMillis(sendFinalResponseInMillis));
@Override
public Mono<PollResponse<CreateCertificateResponse>> apply(PollResponse<CreateCertificateResponse> prePollResponse) {
++count;
if (LocalDateTime.now().isBefore(timeToReturnFinalResponse)) {
int indexForIntermediateResponse = prePollResponse.getValue() == null || prePollResponse.getValue().intermediateResponseIndex >= intermediateOtherPollResponseList.size() ? 0 : prePollResponse.getValue().intermediateResponseIndex;
PollResponse<CreateCertificateResponse> intermediatePollResponse = intermediateOtherPollResponseList.get(indexForIntermediateResponse);
debug(" Service poll function called ", " returning intermediate response status, otherstatus, value " + intermediatePollResponse.getStatus().toString() + "," + intermediatePollResponse.getOtherStatus() + "," + intermediatePollResponse.getValue().response);
intermediatePollResponse.getValue().intermediateResponseIndex = indexForIntermediateResponse + 1;
return Mono.just(intermediatePollResponse);
} else {
debug(" Service poll function called ", " returning final response " + finalPollResponse.getValue().response);
return Mono.just(finalPollResponse);
}
}
};
}

/* Test where SDK Client is subscribed all responses.
* This scenario is setup where source will generate few in-progress response followed by few OTHER responses and finally successfully completed response.
* The sdk client will only subscribe for a specific OTHER response and final successful response.
**/
@Test
public void subscribeToSpecificOtherOperationStatusTest() throws Exception {
PollResponse<CreateCertificateResponse> successPollResponse = new PollResponse<>(OperationStatus.SUCCESSFULLY_COMPLETED, new CreateCertificateResponse("Created : Cert A"));
PollResponse<CreateCertificateResponse> inProgressPollResponse = new PollResponse<>(OperationStatus.IN_PROGRESS, new CreateCertificateResponse("Starting : Cert A"));
PollResponse<CreateCertificateResponse> other1PollResponse = new PollResponse<>("OTHER_1", new CreateCertificateResponse("Starting : Cert A"));
PollResponse<CreateCertificateResponse> other2PollResponse = new PollResponse<>("OTHER_2", new CreateCertificateResponse("Starting : Cert A"));

ArrayList<PollResponse<CreateCertificateResponse>> inProgressPollResponseList = new ArrayList<>();
inProgressPollResponseList.add(inProgressPollResponse);
inProgressPollResponseList.add(inProgressPollResponse);
inProgressPollResponseList.add(other1PollResponse);
inProgressPollResponseList.add(other2PollResponse);
long totalTimeoutInMillis = 1000 * 2;
Duration pollInterval = Duration.ofMillis(totalTimeoutInMillis / 20);

Function<PollResponse<CreateCertificateResponse>, Mono<PollResponse<CreateCertificateResponse>>> pollOperation =
createPollOperation(inProgressPollResponseList,
successPollResponse, totalTimeoutInMillis - pollInterval.toMillis());

Poller<CreateCertificateResponse> createCertPoller = new Poller<>(pollInterval, pollOperation);
Flux<PollResponse<CreateCertificateResponse>> fluxPollResp = createCertPoller.getObserver();
fluxPollResp.subscribe(pr -> {
debug("0 Got Observer() Response " + pr.getStatus().toString() + " " + pr.getOtherStatus() + " " + pr.getValue().response);
});

createCertPoller.getObserver().subscribe(x -> {
debug("1 Got Observer() Response " + x.getStatus().toString() + " " + x.getStatus() + " " + x.getValue().response);
});

// get Specific Event Observer
List<String> observeOtherStates = new ArrayList<>();
observeOtherStates.add("OTHER_1");
observeOtherStates.add("OTHER_2");
List<OperationStatus> observeOperationStates = new ArrayList<>();
observeOperationStates.add(OperationStatus.SUCCESSFULLY_COMPLETED);
Flux<PollResponse<CreateCertificateResponse>> fluxPollRespFiltered = fluxPollResp.filterWhen(tPollResponse -> matchesState(tPollResponse, observeOperationStates, observeOtherStates));
fluxPollResp.subscribe(pr -> {
debug("1 Got Observer() Response " + pr.getStatus().toString() + " " + pr.getOtherStatus() + " " + pr.getValue().response);
});
fluxPollRespFiltered.subscribe(pr -> {
debug("2 Got Observer(SUCCESSFULLY_COMPLETED, OTHER_1,2) Response " + pr.getStatus().toString() + " " + pr.getOtherStatus() + " " + pr.getValue().response);
});

Thread.sleep(totalTimeoutInMillis + 3 * pollInterval.toMillis());
Assert.assertTrue(createCertPoller.block().getStatus() == OperationStatus.SUCCESSFULLY_COMPLETED);
Assert.assertTrue(createCertPoller.getStatus() == OperationStatus.SUCCESSFULLY_COMPLETED);
Assert.assertTrue(createCertPoller.isAutoPollingEnabled());
}

private Mono<Boolean> matchesState(PollResponse<CreateCertificateResponse> currentPollResponse, List<OperationStatus> observeOperationStates, List<String> observeOtherStates) {
List<OperationStatus> operationStates = observeOperationStates != null ? observeOperationStates : new ArrayList<>();
if (currentPollResponse.getStatus() == OperationStatus.OTHER
&& currentPollResponse.getStatus() != null
&& observeOtherStates != null) {
if (observeOtherStates.contains(currentPollResponse.getOtherStatus())) {
return Mono.just(true);
}
} else {
if (operationStates.contains(currentPollResponse.getStatus())) {
return Mono.just(true);
}
}
return Mono.just(false);
}


/* Test where SDK Client is subscribed all responses.
* This scenario is setup where source will generate successful response returned
* after few in-progress response. But the sdk client will stop polling in between
Expand Down Expand Up @@ -327,6 +426,7 @@ private void debug(String... messages) {
public class CreateCertificateResponse {
String response;
HttpResponseException error;
int intermediateResponseIndex;

public CreateCertificateResponse(String respone) {
this.response = respone;
Expand Down