diff --git a/.gitattributes b/.gitattributes index 4d98b206..97828ed2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,6 @@ gradlew binary +gradlew.bat binary +/gradle/wrapper/gradle-wrapper.properties binary .gitattributes text eol=crlf diff --git a/build.gradle b/build.gradle index 2ededc6e..b647e8d1 100644 --- a/build.gradle +++ b/build.gradle @@ -312,3 +312,12 @@ publishing { } } } + +dependencyUpdates.resolutionStrategy = { + componentSelection { rules -> + rules.all { selection -> + if ( ['alpha', 'beta', 'rc', 'cr', 'm'].any { qualifier -> selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/ } ) + selection.reject('only a release candidate') + } + } +} diff --git a/commons/src/main/java/com/jagrosh/jdautilities/commons/async/AsyncFuture.java b/commons/src/main/java/com/jagrosh/jdautilities/commons/async/AsyncFuture.java new file mode 100644 index 00000000..1c39a7a2 --- /dev/null +++ b/commons/src/main/java/com/jagrosh/jdautilities/commons/async/AsyncFuture.java @@ -0,0 +1,6 @@ +package com.jagrosh.jdautilities.commons.async; + +import java.util.concurrent.CompletionStage; +import java.util.concurrent.Future; + +public interface AsyncFuture extends Future, CompletionStage {} diff --git a/commons/src/main/java/com/jagrosh/jdautilities/commons/async/AsyncTask.java b/commons/src/main/java/com/jagrosh/jdautilities/commons/async/AsyncTask.java new file mode 100644 index 00000000..a040817a --- /dev/null +++ b/commons/src/main/java/com/jagrosh/jdautilities/commons/async/AsyncTask.java @@ -0,0 +1,12 @@ +package com.jagrosh.jdautilities.commons.async; + +import java.util.concurrent.CompletableFuture; + +public class AsyncTask extends CompletableFuture implements AsyncFuture +{ + @Override + public CompletableFuture toCompletableFuture() + { + throw new UnsupportedOperationException("Access to the CompletableFuture is not supported."); + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9c942e6d..568c50bf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,20 +1,5 @@ -# -# Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-bin.zip diff --git a/settings.gradle b/settings.gradle index c1d4b23f..3ecec487 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,3 +20,4 @@ include ':commons' include ':doc' include ':examples' include ':menu' +include ':statuspage' diff --git a/statuspage/README.md b/statuspage/README.md new file mode 100644 index 00000000..9ec7dc71 --- /dev/null +++ b/statuspage/README.md @@ -0,0 +1,3 @@ +# Statuspage Package + +This API can be used to retrieve data from [https://status.discordapp.com](https://status.discordapp.com). diff --git a/statuspage/build.gradle b/statuspage/build.gradle new file mode 100644 index 00000000..8de5838b --- /dev/null +++ b/statuspage/build.gradle @@ -0,0 +1,26 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +includeInParent = false + +dependencies { + compile okhttp() + compile json() + compile findbugs() + + compile commons() + + testCompile junit() +} diff --git a/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/StatusPage.java b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/StatusPage.java new file mode 100644 index 00000000..cc83d5de --- /dev/null +++ b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/StatusPage.java @@ -0,0 +1,384 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jagrosh.jdautilities.statuspage; + +import com.jagrosh.jdautilities.commons.async.AsyncFuture; +import com.jagrosh.jdautilities.commons.async.AsyncTask; +import com.jagrosh.jdautilities.statuspage.data.*; +import com.jagrosh.jdautilities.statuspage.endpoints.*; +import okhttp3.*; +import org.json.JSONArray; +import org.json.JSONObject; +import org.json.JSONTokener; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import java.io.IOException; +import java.io.Reader; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +@Immutable +public class StatusPage +{ + @Nonnull + protected static final String URL_API_BASE = "https://status.discordapp.com/api/v2"; + @Nonnull + protected static final String URL_SUMMARY = URL_API_BASE + "/summary.json"; + @Nonnull + protected static final String URL_SERVICE_STATUS = URL_API_BASE + "/status.json"; + @Nonnull + protected static final String URL_COMPONENTS = URL_API_BASE + "/components.json"; + @Nonnull + protected static final String URL_SCHEDULED_INCIDENTS_BASE = URL_API_BASE + "/incidents"; + @Nonnull + protected static final String URL_INCIDENTS_ALL = URL_SCHEDULED_INCIDENTS_BASE + ".json"; + @Nonnull + protected static final String URL_INCIDENTS_UNRESOLVED = URL_SCHEDULED_INCIDENTS_BASE + "/unresolved.json"; + @Nonnull + protected static final String URL_SCHEDULED_MAINTENANCES_BASE = URL_API_BASE + "/scheduled-maintenances"; + @Nonnull + protected static final String URL_SCHEDULED_MAINTENANCES_ALL = URL_SCHEDULED_MAINTENANCES_BASE + ".json"; + @Nonnull + protected static final String URL_SCHEDULED_MAINTENANCES_ACTIVE = URL_SCHEDULED_MAINTENANCES_BASE + "/active.json"; + @Nonnull + protected static final String URL_SCHEDULED_MAINTENANCES_UPCOMING = URL_SCHEDULED_MAINTENANCES_BASE + "/upcoming.json"; + @Nonnull + protected static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + + @Nonnull + protected final OkHttpClient client; + + public StatusPage() + { + this(null); + } + + public StatusPage(@Nullable OkHttpClient client) + { + this.client = client == null ? new OkHttpClient.Builder().build() : client; + } + + /** + * Get a summary of the status page, including a status indicator, component statuses, unresolved incidents, and any upcoming or + * in-progress scheduled maintenances. + */ + @Nonnull + public AsyncFuture getSummary() + { + return get(URL_SUMMARY, this::createSummary); + } + + /** + * Get the components for the page. Each component is listed along with its status - one of operational, + * degraded_performance, partial_outage, or major_outage. + */ + @Nonnull + public AsyncFuture getComponents() + { + return get(URL_COMPONENTS, this::createComponents); + } + + /** + * Get a list of the 50 most recent incidents. This includes all unresolved incidents as described above, + * as well as those in the Resolved and Postmortem state. + */ + @Nonnull + public AsyncFuture getIncidentsAll() + { + return get(URL_INCIDENTS_ALL, this::createIncidents); + } + + /** + * Get a list of any unresolved incidents. This endpoint will only return incidents in the Investigating, Identified, or Monitoring state. + */ + @Nonnull + public AsyncFuture getIncidentsUnresolved() + { + return get(URL_INCIDENTS_UNRESOLVED, this::createIncidents); + } + + /** + * Get a list of the 50 most recent scheduled maintenances. This includes scheduled maintenances as described in the above two endpoints, + * as well as those in the Completed state. + */ + @Nonnull + public AsyncFuture getScheduledMaintenancesAll() + { + return get(URL_SCHEDULED_MAINTENANCES_ALL, this::createScheduledMaintenances); + } + + /** + * Get a list of any active maintenances. This endpoint will only return scheduled maintenances in the In Progress or Verifying state. + */ + @Nonnull + public AsyncFuture getScheduledMaintenancesActive() + { + return get(URL_SCHEDULED_MAINTENANCES_ACTIVE, this::createScheduledMaintenances); + } + + /** + * Get a list of any upcoming maintenances. This endpoint will only return scheduled maintenances still in the Scheduled state. + */ + @Nonnull + public AsyncFuture getScheduledMaintenancesUpcoming() + { + return get(URL_SCHEDULED_MAINTENANCES_UPCOMING, this::createScheduledMaintenances); + } + + /** + * Get the status rollup for the whole page. This endpoint includes an indicator - one of none, minor, major, or critical, + * as well as a human description of the blended component status. Examples of the blended status include + * "All Systems Operational", "Partial System Outage", and "Major Service Outage". + */ + @Nonnull + public AsyncFuture getServiceStatus() + { + return get(URL_SERVICE_STATUS, this::createServiceStatus); + } + + @Nonnull + protected AsyncFuture get(@Nonnull String url, @Nonnull Function funtion) + { + AsyncTask future = new AsyncTask<>(); + + // @formatter:off + Request request = new Request.Builder() + .get() + .url(url) + .header("Content-Type", "application/json") + .build(); + // @formatter:on + + Call call = this.client.newCall(request); + + call.enqueue(new Callback() + { + @Override + public void onResponse(@Nonnull Call call, @Nonnull Response response) + { + ResponseBody body = response.body(); + + if (body == null) + throw new IllegalStateException("response has no body"); + + Reader reader = body.charStream(); + + JSONObject object = new JSONObject(new JSONTokener(reader)); + + T t = funtion.apply(object); + + future.complete(t); + } + + @Override + public void onFailure(@Nonnull Call call, @Nonnull IOException e) + { + future.completeExceptionally(e); + } + }); + + return future; + } + + @Nonnull + protected Summary createSummary(@Nonnull JSONObject object) + { + final JSONArray componentsArray = object.getJSONArray("components"); + final List components = new ArrayList<>(componentsArray.length()); + for (int i = 0; i < componentsArray.length(); i++) + components.add(createComponent(componentsArray.getJSONObject(i))); + final JSONArray incidentsArray = object.getJSONArray("incidents"); + final List incidents = new ArrayList<>(incidentsArray.length()); + for (int i = 0; i < incidentsArray.length(); i++) + incidents.add(createIncident(incidentsArray.getJSONObject(i))); + final JSONArray scheduledMaintenancesArray = object.getJSONArray("scheduled_maintenances"); + final List scheduledMaintenances = new ArrayList<>(scheduledMaintenancesArray.length()); + for (int i = 0; i < scheduledMaintenancesArray.length(); i++) + scheduledMaintenances.add(createScheduledMaintenance(scheduledMaintenancesArray.getJSONObject(i))); + final JSONObject pageObject = object.getJSONObject("page"); + final Page page = createPage(pageObject); + final JSONObject statusObject = object.getJSONObject("status"); + final Status status = createStatus(statusObject); + + return new Summary(components, incidents, page, scheduledMaintenances, status); + } + + @Nonnull + protected Components createComponents(@Nonnull JSONObject object) + { + final JSONObject pageObject = object.getJSONObject("page"); + final Page page = createPage(pageObject); + final JSONArray componentsArray = object.getJSONArray("components"); + final List components = new ArrayList<>(componentsArray.length()); + for (int i = 0; i < componentsArray.length(); i++) + components.add(createComponent(componentsArray.getJSONObject(i))); + + return new Components(page, components); + } + + @Nonnull + protected Incidents createIncidents(@Nonnull JSONObject object) + { + final JSONArray incidentsArray = object.getJSONArray("incidents"); + final List incidents = new ArrayList<>(incidentsArray.length()); + for (int i = 0; i < incidentsArray.length(); i++) + incidents.add(createIncident(incidentsArray.getJSONObject(i))); + final JSONObject pageObject = object.getJSONObject("page"); + final Page page = createPage(pageObject); + + return new Incidents(page, incidents); + } + + @Nonnull + protected ScheduledMaintenances createScheduledMaintenances(@Nonnull JSONObject object) + { + final JSONObject pageObject = object.getJSONObject("page"); + final Page page = createPage(pageObject); + final JSONArray scheduledMaintenancesArray = object.getJSONArray("scheduled_maintenances"); + final List scheduledMaintenances = new ArrayList<>(scheduledMaintenancesArray.length()); + for (int i = 0; i < scheduledMaintenancesArray.length(); i++) + scheduledMaintenances.add(createScheduledMaintenance(scheduledMaintenancesArray.getJSONObject(i))); + + return new ScheduledMaintenances(page, scheduledMaintenances); + } + + @Nonnull + protected ServiceStatus createServiceStatus(@Nonnull JSONObject object) + { + final JSONObject pageObject = object.getJSONObject("page"); + final Page page = createPage(pageObject); + final JSONObject statusObject = object.getJSONObject("status"); + final Status status = createStatus(statusObject); + + return new ServiceStatus(page, status); + } + + @Nonnull + protected Component createComponent(@Nonnull JSONObject object) + { + final String pageId = object.getString("page_id"); + final String updatedAt = object.getString("updated_at"); + final String name = object.getString("name"); + final String createdAt = object.getString("created_at"); + final String description = object.optString("description"); + final String id = object.getString("id"); + final int position = object.getInt("position"); + final String status = object.getString("status"); + final boolean showcase = object.getBoolean("showcase"); + final String groupId = object.optString("group_id"); + final boolean group = object.getBoolean("group"); + final boolean showOnlyIfDegraded = object.getBoolean("only_show_if_degraded"); + + return new Component(pageId, toOffsetDateTime(updatedAt), name, toOffsetDateTime(createdAt), description, id, position, + Component.Status.from(status), showcase, groupId, group, showOnlyIfDegraded); + } + + @Nonnull + protected Incident createIncident(@Nonnull JSONObject object) + { + final String monitoringAt = object.optString("monitoring_at"); + final String pageId = object.getString("page_id"); + final String updatedAt = object.getString("updated_at"); + final String resolvedAt = object.optString("resolved_at"); + final String impact = object.getString("impact"); + final String name = object.getString("name"); + final String createdAt = object.getString("created_at"); + final JSONArray updatesArray = object.getJSONArray("incident_updates"); + final List updates = new ArrayList<>(updatesArray.length()); + for (int i = 0; i < updatesArray.length(); i++) + updates.add(createIncidentUpdate(updatesArray.getJSONObject(i))); + final String id = object.getString("id"); + final String shortlink = object.getString("shortlink"); + final String status = object.getString("status"); + + return new Incident(toOffsetDateTime(monitoringAt), pageId, toOffsetDateTime(updatedAt), toOffsetDateTime(resolvedAt), + Incident.Impact.from(impact), name, toOffsetDateTime(createdAt), updates, id, shortlink, Incident.Status.from(status)); + } + + @Nonnull + protected Incident.Update createIncidentUpdate(@Nonnull JSONObject object) + { + final String incidentId = object.getString("incident_id"); + final String updatedAt = object.getString("updated_at"); + final String createdAt = object.getString("created_at"); + final String id = object.getString("id"); + final String body = object.getString("body"); + final String displayAt = object.getString("display_at"); + final String status = object.getString("status"); + + // There are 3 more fields but they aren't part of the public API + // and I'm unsure what they are used for: + // 'affected_components', 'custom_tweet' and 'deliver_notifications' + + return new Incident.Update(incidentId, toOffsetDateTime(updatedAt), toOffsetDateTime(createdAt), id, body, + toOffsetDateTime(displayAt), Incident.Status.from(status)); + } + + @Nonnull + protected ScheduledMaintenance createScheduledMaintenance(@Nonnull JSONObject object) + { + final String monitoringAt = object.optString("monitoring_at"); + final String pageId = object.getString("page_id"); + final String updatedAt = object.getString("updated_at"); + final String resolvedAt = object.optString("resolved_at"); + final String impact = object.getString("impact"); + final String name = object.getString("name"); + final String createdAt = object.getString("created_at"); + final JSONArray updatesArray = object.getJSONArray("incident_updates"); + final List updates = new ArrayList<>(updatesArray.length()); + for (int i = 0; i < updatesArray.length(); i++) + updates.add(createIncidentUpdate(updatesArray.getJSONObject(i))); + final String id = object.getString("id"); + final String shortlink = object.getString("shortlink"); + final String status = object.getString("status"); + final String scheduledFor = object.getString("updated_at"); + final String scheduledUntil = object.getString("updated_at"); + + return new ScheduledMaintenance(toOffsetDateTime(monitoringAt), pageId, toOffsetDateTime(updatedAt), toOffsetDateTime(resolvedAt), + Incident.Impact.from(impact), name, toOffsetDateTime(createdAt), updates, id, shortlink, + Incident.Status.from(status), toOffsetDateTime(scheduledFor), toOffsetDateTime(scheduledUntil)); + } + + @Nonnull + protected Status createStatus(@Nonnull JSONObject object) + { + final String indicator = object.getString("indicator"); + final String description = object.getString("description"); + + return new Status(Status.Indicator.from(indicator), description); + } + + @Nonnull + protected Page createPage(@Nonnull JSONObject object) + { + final String name = object.getString("name"); + final String id = object.getString("id"); + final String url = object.getString("url"); + final String updatedAt = object.getString("updated_at"); + + return new Page(name, id, url, toOffsetDateTime(updatedAt)); + } + + protected OffsetDateTime toOffsetDateTime(String time) + { + return time == null || time.isEmpty() ? null : OffsetDateTime.parse(time, DATE_TIME_FORMATTER); + } +} diff --git a/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/Component.java b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/Component.java new file mode 100644 index 00000000..cca58c06 --- /dev/null +++ b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/Component.java @@ -0,0 +1,172 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jagrosh.jdautilities.statuspage.data; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import java.time.OffsetDateTime; +import java.util.HashMap; +import java.util.Map; + +@Immutable +public class Component +{ + @Nonnull + protected final String pageId; + @Nonnull + protected final OffsetDateTime updatedAt; + @Nonnull + protected final String name; + @Nonnull + protected final OffsetDateTime createdAt; + @Nullable + protected final String description; + @Nonnull + protected final String id; + protected final int position; + @Nonnull + protected final Status status; + protected final boolean showcase; + @Nullable + protected final String groupId; + protected final boolean group; + protected final boolean showOnlyIfDegraded; + + public Component(@Nonnull String pageId, @Nonnull OffsetDateTime updatedAt, @Nonnull String name, @Nonnull OffsetDateTime createdAt, @Nullable String description, @Nonnull String id, int position, @Nonnull Status status, boolean showcase, @Nullable String groupId, boolean group, boolean showOnlyIfDegraded) + { + this.pageId = pageId; + this.updatedAt = updatedAt; + this.name = name; + this.createdAt = createdAt; + this.description = description; + this.id = id; + this.position = position; + this.status = status; + this.showcase = showcase; + this.groupId = groupId; + this.group = group; + this.showOnlyIfDegraded = showOnlyIfDegraded; + } + + @Nonnull + public String getPageId() + { + return pageId; + } + + @Nonnull + public OffsetDateTime getUpdatedAt() + { + return updatedAt; + } + + @Nonnull + public String getName() + { + return name; + } + + @Nonnull + public OffsetDateTime getCreatedAt() + { + return createdAt; + } + + @Nullable + public String getDescription() + { + return description; + } + + @Nonnull + public String getId() + { + return id; + } + + public int getPosition() + { + return position; + } + + @Nonnull + public Status getStatus() + { + return status; + } + + public boolean isShowcase() + { + return showcase; + } + + @Nullable + public String getGroupId() + { + return groupId; + } + + public boolean isGroup() + { + return group; + } + + public boolean isShowOnlyIfDegraded() + { + return showOnlyIfDegraded; + } + + public enum Status + { + OPERATIONAL("operational"), + DEGRADED_PERFORMANCE("degraded_performance"), + PARTIAL_OUTAGE("partial_outage"), + MAJOR_OUTAGE("major_outage"), + + UNKNOWN(""); + + @Nonnull + private static final Map MAP = new HashMap<>(); + + static + { + for (Status status : Status.values()) + if (MAP.put(status.getKey(), status) != null) + throw new IllegalStateException("Duplicate key: " + status.getKey()); + } + + @Nonnull + private final String key; + + Status(@Nonnull String key) + { + this.key = key; + } + + @Nonnull + public static Status from(@Nullable String key) + { + return MAP.getOrDefault(key, UNKNOWN); + } + + @Nonnull + public String getKey() + { + return key; + } + } +} diff --git a/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/Incident.java b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/Incident.java new file mode 100644 index 00000000..2e98337e --- /dev/null +++ b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/Incident.java @@ -0,0 +1,285 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jagrosh.jdautilities.statuspage.data; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import java.time.OffsetDateTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Immutable +public class Incident +{ + @Nullable + protected final OffsetDateTime monitoringAt; + @Nonnull + protected final String pageId; + @Nonnull + protected final OffsetDateTime updatedAt; + @Nullable + protected final OffsetDateTime resolvedAt; + @Nonnull + protected final Impact impact; + @Nonnull + protected final String name; + @Nonnull + protected final OffsetDateTime createdAt; + @Nonnull + protected final List updates; + @Nonnull + protected final String id; + @Nonnull + protected final String shortlink; + @Nonnull + protected final Status status; + + public Incident(@Nullable OffsetDateTime monitoringAt, @Nonnull String pageId, @Nonnull OffsetDateTime updatedAt, @Nullable OffsetDateTime resolvedAt, @Nonnull Impact impact, @Nonnull String name, @Nonnull OffsetDateTime createdAt, @Nonnull List updates, @Nonnull String id, @Nonnull String shortlink, @Nonnull Status status) + { + this.monitoringAt = monitoringAt; + this.pageId = pageId; + this.updatedAt = updatedAt; + this.resolvedAt = resolvedAt; + this.impact = impact; + this.name = name; + this.createdAt = createdAt; + this.updates = Collections.unmodifiableList(updates); + this.id = id; + this.shortlink = shortlink; + this.status = status; + } + + @Nullable + public OffsetDateTime getMonitoringAt() + { + return monitoringAt; + } + + @Nonnull + public String getPageId() + { + return pageId; + } + + @Nonnull + public OffsetDateTime getUpdatedAt() + { + return updatedAt; + } + + @Nullable + public OffsetDateTime getResolvedAt() + { + return resolvedAt; + } + + @Nonnull + public Impact getImpact() + { + return impact; + } + + @Nonnull + public String getName() + { + return name; + } + + @Nonnull + public OffsetDateTime getCreatedAt() + { + return createdAt; + } + + @Nonnull + public List getUpdates() + { + return updates; + } + + @Nonnull + public String getId() + { + return id; + } + + @Nonnull + public String getShortlink() + { + return shortlink; + } + + @Nonnull + public Status getStatus() + { + return status; + } + + public enum Status + { + INVESTIGATING("investigating"), + IDENTIFIED("identified"), + MONITORING("monitoring"), + RESOLVED("resolved"), + POSTMORTEM("postmortem"), + + UNKNOWN(""); + + @Nonnull + private static final Map MAP = new HashMap<>(); + + static + { + for (Status status : Status.values()) + if (MAP.put(status.getKey(), status) != null) + throw new IllegalStateException("Duplicate key: " + status.getKey()); + } + + @Nonnull + private final String key; + + Status(@Nonnull String key) + { + this.key = key; + } + + @Nonnull + public static Status from(@Nullable String key) + { + return MAP.getOrDefault(key, UNKNOWN); + } + + @Nonnull + public String getKey() + { + return key; + } + } + + public enum Impact + { + NONE("none"), + MINOR("minor"), + MAJOR("major"), + CRITICAL("critical"), + + UNKNOWN(""); + + @Nonnull + private static final Map MAP = new HashMap<>(); + + static + { + for (Impact impact : Impact.values()) + if (MAP.put(impact.getKey(), impact) != null) + throw new IllegalStateException("Duplicate key: " + impact.getKey()); + } + + @Nonnull + private final String key; + + Impact(@Nonnull String key) + { + this.key = key; + } + + @Nonnull + public static Impact from(@Nullable String key) + { + return MAP.getOrDefault(key, UNKNOWN); + } + + @Nonnull + public String getKey() + { + return key; + } + } + + public static class Update + { + @Nonnull + protected final String incidentId; + @Nonnull + protected final OffsetDateTime updatedAt; + @Nonnull + protected final OffsetDateTime createdAt; + @Nonnull + protected final String id; + @Nonnull + protected final String body; + @Nonnull + protected final OffsetDateTime displayAt; + @Nonnull + protected final Status status; + + public Update(@Nonnull String incidentId, @Nonnull OffsetDateTime updatedAt, @Nonnull OffsetDateTime createdAt, @Nonnull String id, @Nonnull String body, @Nonnull OffsetDateTime displayAt, @Nonnull Status status) + { + this.incidentId = incidentId; + this.updatedAt = updatedAt; + this.createdAt = createdAt; + this.id = id; + this.body = body; + this.displayAt = displayAt; + this.status = status; + } + + @Nonnull + public String getIncidentId() + { + return incidentId; + } + + @Nonnull + public OffsetDateTime getUpdatedAt() + { + return updatedAt; + } + + @Nonnull + public OffsetDateTime getCreatedAt() + { + return createdAt; + } + + @Nonnull + public String getId() + { + return id; + } + + @Nonnull + public String getBody() + { + return body; + } + + @Nonnull + public OffsetDateTime getDisplayAt() + { + return displayAt; + } + + @Nonnull + public Status getStatus() + { + return status; + } + } +} diff --git a/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/Page.java b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/Page.java new file mode 100644 index 00000000..15832efa --- /dev/null +++ b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/Page.java @@ -0,0 +1,65 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jagrosh.jdautilities.statuspage.data; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; +import java.time.OffsetDateTime; + +@Immutable +public class Page +{ + @Nonnull + protected final String name; + @Nonnull + protected final String id; + @Nonnull + protected final String url; + @Nonnull + protected final OffsetDateTime updatedAt; + + public Page(@Nonnull String name, @Nonnull String id, @Nonnull String url, @Nonnull OffsetDateTime updatedAt) + { + this.name = name; + this.id = id; + this.url = url; + this.updatedAt = updatedAt; + } + + @Nonnull + public String getName() + { + return name; + } + + @Nonnull + public String getId() + { + return id; + } + + @Nonnull + public String getUrl() + { + return url; + } + + @Nonnull + public OffsetDateTime getUpdatedAt() + { + return updatedAt; + } +} diff --git a/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/ScheduledMaintenance.java b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/ScheduledMaintenance.java new file mode 100644 index 00000000..47747fe2 --- /dev/null +++ b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/ScheduledMaintenance.java @@ -0,0 +1,50 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jagrosh.jdautilities.statuspage.data; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; +import java.time.OffsetDateTime; +import java.util.List; + +@Immutable +public class ScheduledMaintenance extends Incident +{ + @Nonnull + protected final OffsetDateTime scheduledFor; + @Nonnull + protected final OffsetDateTime scheduledUntil; + + public ScheduledMaintenance(OffsetDateTime monitoringAt, @Nonnull String pageId, @Nonnull OffsetDateTime updatedAt, OffsetDateTime resolvedAt, @Nonnull Impact impact, @Nonnull String name, @Nonnull OffsetDateTime createdAt, @Nonnull List updates, @Nonnull String id, @Nonnull String shortlink, @Nonnull Status status, @Nonnull OffsetDateTime scheduledFor, @Nonnull OffsetDateTime scheduledUntil) + { + super(monitoringAt, pageId, updatedAt, resolvedAt, impact, name, createdAt, updates, id, shortlink, status); + + this.scheduledFor = scheduledFor; + this.scheduledUntil = scheduledUntil; + } + + @Nonnull + public OffsetDateTime getScheduledFor() + { + return scheduledFor; + } + + @Nonnull + public OffsetDateTime getScheduledUntil() + { + return scheduledUntil; + } +} diff --git a/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/Status.java b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/Status.java new file mode 100644 index 00000000..ed887b57 --- /dev/null +++ b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/data/Status.java @@ -0,0 +1,88 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jagrosh.jdautilities.statuspage.data; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import java.util.HashMap; +import java.util.Map; + +@Immutable +public class Status +{ + @Nonnull + protected final Indicator indicator; + @Nonnull + protected final String description; + + public Status(@Nonnull Indicator indicator, @Nonnull String description) + { + this.indicator = indicator; + this.description = description; + } + + @Nonnull + public Indicator getIndicator() + { + return indicator; + } + + @Nonnull + public String getDescription() + { + return description; + } + + public enum Indicator + { + NONE("none"), + MINOR("minor"), + MAJOR("major"), + CRITICAL("critical"), + + UNKNOWN(""); + + private static final Map MAP = new HashMap<>(); + + static + { + for (Indicator indicator : Indicator.values()) + if (MAP.put(indicator.getKey(), indicator) != null) + throw new IllegalStateException("Duplicate key: " + indicator.getKey()); + } + + @Nonnull + private final String key; + + Indicator(@Nonnull String key) + { + this.key = key; + } + + @Nonnull + public static Indicator from(@Nullable String key) + { + return MAP.getOrDefault(key, UNKNOWN); + } + + @Nonnull + public String getKey() + { + return key; + } + } +} diff --git a/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/Components.java b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/Components.java new file mode 100644 index 00000000..b9a601e5 --- /dev/null +++ b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/Components.java @@ -0,0 +1,51 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jagrosh.jdautilities.statuspage.endpoints; + +import com.jagrosh.jdautilities.statuspage.data.Component; +import com.jagrosh.jdautilities.statuspage.data.Page; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; +import java.util.Collections; +import java.util.List; + +@Immutable +public class Components +{ + @Nonnull + protected final Page page; + @Nonnull + protected final List components; + + public Components(@Nonnull Page page, @Nonnull List components) + { + this.page = page; + this.components = Collections.unmodifiableList(components); + } + + @Nonnull + public Page getPage() + { + return page; + } + + @Nonnull + public List getComponents() + { + return components; + } +} diff --git a/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/Incidents.java b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/Incidents.java new file mode 100644 index 00000000..344a9a70 --- /dev/null +++ b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/Incidents.java @@ -0,0 +1,51 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jagrosh.jdautilities.statuspage.endpoints; + +import com.jagrosh.jdautilities.statuspage.data.Incident; +import com.jagrosh.jdautilities.statuspage.data.Page; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; +import java.util.Collections; +import java.util.List; + +@Immutable +public class Incidents +{ + @Nonnull + protected final Page page; + @Nonnull + protected final List incidents; + + public Incidents(@Nonnull Page page, @Nonnull List incidents) + { + this.page = page; + this.incidents = Collections.unmodifiableList(incidents); + } + + @Nonnull + public Page getPage() + { + return page; + } + + @Nonnull + public List getIncidents() + { + return incidents; + } +} diff --git a/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/ScheduledMaintenances.java b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/ScheduledMaintenances.java new file mode 100644 index 00000000..f09a8942 --- /dev/null +++ b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/ScheduledMaintenances.java @@ -0,0 +1,50 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jagrosh.jdautilities.statuspage.endpoints; + +import com.jagrosh.jdautilities.statuspage.data.Page; +import com.jagrosh.jdautilities.statuspage.data.ScheduledMaintenance; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; +import java.util.List; + +@Immutable +public class ScheduledMaintenances +{ + @Nonnull + protected final Page page; + @Nonnull + protected final List scheduledMaintenances; + + public ScheduledMaintenances(@Nonnull Page page, @Nonnull List scheduledMaintenances) + { + this.page = page; + this.scheduledMaintenances = scheduledMaintenances; + } + + @Nonnull + public Page getPage() + { + return page; + } + + @Nonnull + public List getScheduledMaintenances() + { + return scheduledMaintenances; + } +} diff --git a/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/ServiceStatus.java b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/ServiceStatus.java new file mode 100644 index 00000000..6bd15176 --- /dev/null +++ b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/ServiceStatus.java @@ -0,0 +1,49 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jagrosh.jdautilities.statuspage.endpoints; + +import com.jagrosh.jdautilities.statuspage.data.Page; +import com.jagrosh.jdautilities.statuspage.data.Status; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; + +@Immutable +public class ServiceStatus +{ + @Nonnull + protected final Page page; + @Nonnull + protected final Status status; + + public ServiceStatus(@Nonnull Page page, @Nonnull Status status) + { + this.page = page; + this.status = status; + } + + @Nonnull + public Page getPage() + { + return page; + } + + @Nonnull + public Status getStatus() + { + return status; + } +} diff --git a/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/Summary.java b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/Summary.java new file mode 100644 index 00000000..d045f386 --- /dev/null +++ b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/endpoints/Summary.java @@ -0,0 +1,77 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jagrosh.jdautilities.statuspage.endpoints; + +import com.jagrosh.jdautilities.statuspage.data.*; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; +import java.util.Collections; +import java.util.List; + +@Immutable +public class Summary +{ + @Nonnull + protected final List components; + @Nonnull + protected final List incidents; + @Nonnull + protected final Page page; + @Nonnull + protected final List scheduledMaintenances; + @Nonnull + protected final Status status; + + public Summary(@Nonnull List components, @Nonnull List incidents, @Nonnull Page page, @Nonnull List scheduledMaintenances, @Nonnull Status status) + { + this.components = Collections.unmodifiableList(components); + this.incidents = Collections.unmodifiableList(incidents); + this.page = page; + this.scheduledMaintenances = Collections.unmodifiableList(scheduledMaintenances); + this.status = status; + } + + @Nonnull + public List getComponents() + { + return components; + } + + @Nonnull + public List getIncidents() + { + return incidents; + } + + @Nonnull + public Page getPage() + { + return page; + } + + @Nonnull + public List getScheduledMaintenances() + { + return scheduledMaintenances; + } + + @Nonnull + public Status getStatus() + { + return status; + } +} diff --git a/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/package-info.java b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/package-info.java new file mode 100644 index 00000000..07f80493 --- /dev/null +++ b/statuspage/src/main/java/com/jagrosh/jdautilities/statuspage/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2018 John Grosh (jagrosh) & Kaidan Gustave (TheMonitorLizard) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * StatusPage package.
+ */ +package com.jagrosh.jdautilities.statuspage; diff --git a/statuspage/src/test/java/com/jagrosh/jdautilities/statuspage/tests/StatusPageTest.java b/statuspage/src/test/java/com/jagrosh/jdautilities/statuspage/tests/StatusPageTest.java new file mode 100644 index 00000000..ee893970 --- /dev/null +++ b/statuspage/src/test/java/com/jagrosh/jdautilities/statuspage/tests/StatusPageTest.java @@ -0,0 +1,57 @@ +package com.jagrosh.jdautilities.statuspage.tests; + +import com.jagrosh.jdautilities.statuspage.StatusPage; +import org.junit.Test; + +public class StatusPageTest +{ + public final StatusPage statusPage = new StatusPage(); + + @Test + public void testComponents() throws Exception + { + statusPage.getComponents().get(); + } + + @Test + public void testIncidentsAll() throws Exception + { + statusPage.getIncidentsAll().get(); + } + + @Test + public void testIncidentsUnresolved() throws Exception + { + statusPage.getIncidentsUnresolved().get(); + } + + @Test + public void testScheduledMaintenancesActive() throws Exception + { + statusPage.getScheduledMaintenancesActive().get(); + } + + @Test + public void testScheduledMaintenancesAll() throws Exception + { + statusPage.getScheduledMaintenancesAll().get(); + } + + @Test + public void testScheduledMaintenancesUpcoming() throws Exception + { + statusPage.getScheduledMaintenancesUpcoming().get(); + } + + @Test + public void testServiceStatus() throws Exception + { + statusPage.getServiceStatus().get(); + } + + @Test + public void testSummary() throws Exception + { + statusPage.getSummary().get(); + } +}