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

[openuv] Enhance server side error handling #12958

Merged
merged 7 commits into from
Jun 19, 2022
Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -59,6 +59,7 @@
@NonNullByDefault
public class OpenUVBridgeHandler extends BaseBridgeHandler {
private static final String QUERY_URL = "https://api.openuv.io/api/v1/uv?lat=%s&lng=%s&alt=%s";
private static final int RECONNECT_DELAY_MIN = 5;
private static final int REQUEST_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(30);

private final Logger logger = LoggerFactory.getLogger(OpenUVBridgeHandler.class);
Expand All @@ -69,6 +70,7 @@ public class OpenUVBridgeHandler extends BaseBridgeHandler {
private final LocaleProvider localeProvider;

private Optional<ScheduledFuture<?>> reconnectJob = Optional.empty();
private boolean keyVerified;

public OpenUVBridgeHandler(Bridge bridge, LocationProvider locationProvider, TranslationProvider i18nProvider,
LocaleProvider localeProvider, Gson gson) {
Expand All @@ -82,6 +84,7 @@ public OpenUVBridgeHandler(Bridge bridge, LocationProvider locationProvider, Tra
@Override
public void initialize() {
logger.debug("Initializing OpenUV API bridge handler.");
keyVerified = false;
BridgeConfiguration config = getConfigAs(BridgeConfiguration.class);
if (config.apikey.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
Expand All @@ -94,8 +97,7 @@ public void initialize() {

@Override
public void dispose() {
reconnectJob.ifPresent(job -> job.cancel(true));
reconnectJob = Optional.empty();
freeReconnectJob();
}

@Override
Expand All @@ -113,6 +115,8 @@ private void initiateConnexion() {
}

public @Nullable OpenUVResult getUVData(String latitude, String longitude, String altitude) {
String statusMessage = "";
ThingStatusDetail statusDetail = ThingStatusDetail.COMMUNICATION_ERROR;
String url = String.format(QUERY_URL, latitude, longitude, altitude);
String jsonData = "";
try {
Expand All @@ -122,33 +126,52 @@ private void initiateConnexion() {
String error = uvResponse.getError();
if (error == null) {
updateStatus(ThingStatus.ONLINE);
keyVerified = true;
return uvResponse.getResult();
}
throw new OpenUVException(error);
}
} catch (JsonSyntaxException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
String.format("Invalid json received when calling `%s` : %s", url, jsonData));
if (jsonData.contains("MongoError")) {
statusMessage = String.format("@text/offline.comm-error-faultly-service [ \"%d\" ]",
RECONNECT_DELAY_MIN);
scheduleReconnectJob(RECONNECT_DELAY_MIN);
} else {
statusDetail = ThingStatusDetail.NONE;
statusMessage = String.format("@text/offline.invalid-json [ \"%s\", \"%s\" ]", url, jsonData);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A relatively small message is expected (it will be displayed in UI). Is your jsonData small ?
If not, maybe you shoudl add a specific DEBUG log entry to log jsonData

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Size of invalid json is not easy to predict, so I take your proposal.

}
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
statusMessage = e.getMessage();
} catch (OpenUVException e) {
if (e.isQuotaError()) {
LocalDateTime tomorrowMidnight = LocalDate.now().plusDays(1).atStartOfDay().plusMinutes(2);

updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, String
.format("@text/offline.comm-error-quota-exceeded [ \"%s\" ]", tomorrowMidnight.toString()));

reconnectJob = Optional.of(scheduler.schedule(this::initiateConnexion,
Duration.between(LocalDateTime.now(), tomorrowMidnight).toMinutes(), TimeUnit.MINUTES));
} else {
updateStatus(ThingStatus.OFFLINE,
e.isApiKeyError() ? ThingStatusDetail.CONFIGURATION_ERROR : ThingStatusDetail.NONE,
e.getMessage());
LocalDateTime nextMidnight = LocalDate.now().plusDays(1).atStartOfDay().plusMinutes(2);
statusMessage = String.format("@text/offline.comm-error-quota-exceeded [ \"%s\" ]",
nextMidnight.toString());
scheduleReconnectJob(Duration.between(LocalDateTime.now(), nextMidnight).toMinutes());
} else if (e.isApiKeyError()) {
if (keyVerified) {
statusMessage = String.format("@text/offline.api-key-not-recognized [ \"%d\" ]",
RECONNECT_DELAY_MIN);
scheduleReconnectJob(RECONNECT_DELAY_MIN);
} else {
statusDetail = ThingStatusDetail.CONFIGURATION_ERROR;
}
}
}
updateStatus(ThingStatus.OFFLINE, statusDetail, statusMessage);
return null;
}

private void scheduleReconnectJob(long delay) {
freeReconnectJob();
reconnectJob = Optional.of(scheduler.schedule(this::initiateConnexion, delay, TimeUnit.MINUTES));
}

private void freeReconnectJob() {
reconnectJob.ifPresent(job -> job.cancel(true));
reconnectJob = Optional.empty();
}

@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Set.of(OpenUVDiscoveryService.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ channel-type.config.openuv.SafeExposure.index.option.VI = Black
offline.config-error-unknown-apikey = Parameter 'apikey' must be configured.
offline.config-error-invalid-refresh = Parameter 'refresh' must be higher than 3 minutes to stay in free API plan.
offline.comm-error-quota-exceeded = Quota Exceeded, going OFFLINE for today, will retry at : {0}
offline.comm-error-faultly-service = Service not responding, will reconnect in {0} minutes
offline.invalid-json = Invalid JSON received when calling `{0}`: {1}
offline.api-key-not-recognized = Service error while API key is known correct, will reconnect in {0} minutes

# discovery result

Expand Down