diff --git a/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/EntityRepository.java b/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/EntityRepository.java index 6ea309309c4..b41c4f61497 100644 --- a/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/EntityRepository.java +++ b/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/EntityRepository.java @@ -520,7 +520,8 @@ public abstract class EntityRepository { newVersion = Math.round((oldVersion + 0.1) * 10.0) / 10.0; } LOG.info( - "{}->{} - Fields added {}, updated {}, deleted {}", + "{} {}->{} - Fields added {}, updated {}, deleted {}", + original.getId(), oldVersion, newVersion, changeDescription.getFieldsAdded(), @@ -531,7 +532,7 @@ public abstract class EntityRepository { return !newVersion.equals(oldVersion); } - private boolean fieldsChanged() { + public boolean fieldsChanged() { return !changeDescription.getFieldsAdded().isEmpty() || !changeDescription.getFieldsUpdated().isEmpty() || !changeDescription.getFieldsDeleted().isEmpty(); diff --git a/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/WebhookRepository.java b/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/WebhookRepository.java index 584774a387d..4d159392a13 100644 --- a/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/WebhookRepository.java +++ b/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/WebhookRepository.java @@ -119,7 +119,8 @@ public class WebhookRepository extends EntityRepository { } public void addWebhookPublisher(Webhook webhook) { - if (!webhook.getEnabled()) { // Only add webhook that is enabled + if (!webhook.getEnabled()) { // Only add webhook that is enabled for publishing events + webhook.setStatus(Status.NOT_STARTED); return; } WebhookPublisher publisher = new WebhookPublisher(webhook); @@ -130,7 +131,7 @@ public class WebhookRepository extends EntityRepository { } public void updateWebhookPublisher(Webhook webhook) throws InterruptedException { - if (webhook.getEnabled()) { // Only add webhook that is enabled + if (webhook.getEnabled()) { // Only add webhook that is enabled for publishing // If there was a previous webhook either in disabled state or stopped due // to errors, update it and restart publishing WebhookPublisher previousPublisher = getPublisher(webhook.getId()); @@ -142,7 +143,7 @@ public class WebhookRepository extends EntityRepository { // Update the existing publisher Status status = previousPublisher.getWebhook().getStatus(); previousPublisher.updateWebhook(webhook); - if (status != Status.SUCCESS && status != Status.AWAITING_RETRY) { + if (status != Status.STARTED && status != Status.AWAITING_RETRY) { // Restart the previously stopped publisher (in states notStarted, error, retryLimitReached) BatchEventProcessor processor = EventPubSub.addEventHandler(previousPublisher); previousPublisher.setProcessor(processor); @@ -160,7 +161,7 @@ public class WebhookRepository extends EntityRepository { publisher.getProcessor().halt(); publisher.awaitShutdown(); EventPubSub.removeProcessor(publisher.getProcessor()); - LOG.info("Webhook publisher deleted {}", publisher.getWebhook()); + LOG.info("Webhook publisher deleted for {}", publisher.getWebhook().getName()); } webhookPublisherMap.remove(id); } @@ -367,9 +368,9 @@ public class WebhookRepository extends EntityRepository { // 2xx response means call back is successful if (response.getStatus() >= 200 && response.getStatus() < 300) { // All 2xx responses batch.clear(); - if (webhook.getStatus() != Status.SUCCESS) { - webhook.getFailureDetails().setLastSuccessfulAt(changeEventHolder.get().getDateTime().getTime()); - setStatus(Status.SUCCESS, null, null, null, null); + webhook.getFailureDetails().setLastSuccessfulAt(changeEventHolder.get().getDateTime().getTime()); + if (webhook.getStatus() != Status.STARTED) { + setStatus(Status.STARTED, null, null, null, null); } // 3xx response/redirection is not allowed for callback. Set the webhook state as in error } else if (response.getStatus() >= 300 && response.getStatus() < 400) { @@ -495,16 +496,31 @@ public class WebhookRepository extends EntityRepository { @Override public void entitySpecificUpdate() throws IOException { - recordChange("enabled", original.getEntity().getEnabled(), updated.getEntity().getEnabled()); - recordChange("status", original.getEntity().getStatus(), updated.getEntity().getStatus()); - recordChange("endPoint", original.getEntity().getEndpoint(), updated.getEntity().getEndpoint()); - recordChange("batchSize", original.getEntity().getBatchSize(), updated.getEntity().getBatchSize()); - recordChange( - "failureDetails", - original.getEntity().getFailureDetails(), - updated.getEntity().getFailureDetails(), - true, - failureDetailsMatch); + Webhook origWebhook = original.getEntity(); + Webhook updatedWebhook = updated.getEntity(); + + recordChange("enabled", origWebhook.getEnabled(), updatedWebhook.getEnabled()); + recordChange("status", origWebhook.getStatus(), updatedWebhook.getStatus()); + recordChange("endPoint", origWebhook.getEndpoint(), updatedWebhook.getEndpoint()); + recordChange("batchSize", origWebhook.getBatchSize(), updatedWebhook.getBatchSize()); + if (fieldsChanged()) { + // If updating the other fields, opportunistically use it to capture failure details + WebhookPublisher publisher = WebhookRepository.this.getPublisher(origWebhook.getId()); + if (publisher != null && updatedWebhook != publisher.getWebhook()) { + updatedWebhook + .withStatus(publisher.getWebhook().getStatus()) + .withFailureDetails(publisher.getWebhook().getFailureDetails()); + if (updatedWebhook.getEnabled() == false) { + updatedWebhook.setStatus(Status.NOT_STARTED); + } + } + recordChange( + "failureDetails", + origWebhook.getFailureDetails(), + updatedWebhook.getFailureDetails(), + true, + failureDetailsMatch); + } } } } diff --git a/catalog-rest-service/src/main/java/org/openmetadata/catalog/resources/events/WebhookResource.java b/catalog-rest-service/src/main/java/org/openmetadata/catalog/resources/events/WebhookResource.java index 7d7d2254a9c..a0855e042e2 100644 --- a/catalog-rest-service/src/main/java/org/openmetadata/catalog/resources/events/WebhookResource.java +++ b/catalog-rest-service/src/main/java/org/openmetadata/catalog/resources/events/WebhookResource.java @@ -234,7 +234,7 @@ public class WebhookResource { throws IOException { SecurityUtil.checkAdminOrBotRole(authorizer, securityContext); Webhook webhook = getWebhook(securityContext, create); - webhook.setStatus(webhook.getEnabled() ? Status.SUCCESS : Status.NOT_STARTED); + webhook.setStatus(webhook.getEnabled() ? Status.STARTED : Status.NOT_STARTED); webhook = dao.create(uriInfo, webhook); dao.addWebhookPublisher(webhook); return Response.created(webhook.getHref()).entity(webhook).build(); @@ -259,7 +259,7 @@ public class WebhookResource { // SecurityUtil.checkAdminOrBotRole(authorizer, securityContext); // Table table = getTable(securityContext, create); Webhook webhook = getWebhook(securityContext, create); - webhook.setStatus(webhook.getEnabled() ? Status.SUCCESS : Status.NOT_STARTED); + webhook.setStatus(webhook.getEnabled() ? Status.STARTED : Status.NOT_STARTED); PutResponse putResponse = dao.createOrUpdate(uriInfo, webhook); dao.updateWebhookPublisher(webhook); return putResponse.toResponse(); diff --git a/catalog-rest-service/src/main/java/org/openmetadata/catalog/util/EntityUtil.java b/catalog-rest-service/src/main/java/org/openmetadata/catalog/util/EntityUtil.java index c7d0dbc769f..9843784d834 100644 --- a/catalog-rest-service/src/main/java/org/openmetadata/catalog/util/EntityUtil.java +++ b/catalog-rest-service/src/main/java/org/openmetadata/catalog/util/EntityUtil.java @@ -109,9 +109,9 @@ public final class EntityUtil { public static final BiPredicate mlFeatureMatch = MlFeature::equals; public static final BiPredicate mlHyperParameterMatch = MlHyperParameter::equals; public static final BiPredicate failureDetailsMatch = - (failureDetails1, failureDetails2) -> - failureDetails1.getLastFailedAt().equals(failureDetails2.getLastFailedAt()) && - failureDetails1.getLastSuccessfulAt().equals(failureDetails2.getLastSuccessfulAt()); + (failureDetails1, failureDetails2) -> + Objects.equals(failureDetails2.getLastFailedAt(), failureDetails1.getLastFailedAt()) + && Objects.equals(failureDetails2.getLastSuccessfulAt(), failureDetails1.getLastSuccessfulAt()); private EntityUtil() {} diff --git a/catalog-rest-service/src/main/resources/json/schema/entity/events/webhook.json b/catalog-rest-service/src/main/resources/json/schema/entity/events/webhook.json index 28a90acba31..de36a709717 100644 --- a/catalog-rest-service/src/main/resources/json/schema/entity/events/webhook.json +++ b/catalog-rest-service/src/main/resources/json/schema/entity/events/webhook.json @@ -60,16 +60,15 @@ "type": "string" }, "status": { - "description": "Status is `notStarted`, when webhook was created with `enabled` set to false and it never started publishing events. Status is `success` on 200 response to notifications. Status is `error` on bad callback URL, connection failures, and `1xx`, `3xx` response. Status is `awaitingRetry` when previous attempt at delivery timed out or received `4xx`, `5xx` response. Status is `retryLimitReached` after all retries fail.", + "description": "Status is `notStarted`, when webhook was created with `enabled` set to false and it never started publishing events. Status is `started` when webhook is normally functioning and 200 OK response was received for callback notification. Status is `failed` on bad callback URL, connection failures, `1xx`, and `3xx` response was received for callback notification. Status is `awaitingRetry` when previous attempt at callback timed out or received `4xx`, `5xx` response. Status is `retryLimitReached` after all retries fail.", "type": "string", "enum": [ "notStarted", - "success", + "started", "failed", "awaitingRetry", "retryLimitReached" - ], - "default": "success" + ] }, "failureDetails": { "description": "Failure details are set only when `status` is not `success`", diff --git a/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/events/WebhookCallbackResource.java b/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/events/WebhookCallbackResource.java index 9d3ae21b864..829b1bd6421 100644 --- a/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/events/WebhookCallbackResource.java +++ b/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/events/WebhookCallbackResource.java @@ -31,6 +31,7 @@ public class WebhookCallbackResource { public static final Logger LOG = LoggerFactory.getLogger(WebhookCallbackResource.class); private final AtomicInteger counter = new AtomicInteger(); private volatile long counterStartTime; + private volatile long counterLatestTime; private final ConcurrentLinkedQueue changeEvents = new ConcurrentLinkedQueue<>(); private final ConcurrentLinkedQueue changeEventsSlowServer = new ConcurrentLinkedQueue<>(); @@ -60,14 +61,27 @@ public class WebhookCallbackResource { if (counter.get() == 0) { counterStartTime = events.getData().get(0).getDateTime().getTime(); } + counterLatestTime = events.getData().get(0).getDateTime().getTime(); counter.incrementAndGet(); LOG.info("callback /counter received event. Current count {}", counter.get()); return Response.ok().build(); } - public int getCount() { return counter.get(); } - public void resetCount() { counter.set(0); } - public long getCountStartTime() { return counterStartTime; } + public int getCount() { + return counter.get(); + } + + public void resetCount() { + counter.set(0); + } + + public long getCounterStartTime() { + return counterStartTime; + } + + public long getCounterLatestTime() { + return counterLatestTime; + } /** Webhook endpoint that immediately responds to callback. The events received are ignored */ @POST diff --git a/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/events/WebhookResourceTest.java b/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/events/WebhookResourceTest.java index 89f4df3c401..1e515f3c392 100644 --- a/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/events/WebhookResourceTest.java +++ b/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/events/WebhookResourceTest.java @@ -30,7 +30,6 @@ import java.util.concurrent.ConcurrentLinkedQueue; import javax.ws.rs.core.Response; import org.apache.http.client.HttpResponseException; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; import org.openmetadata.catalog.Entity; import org.openmetadata.catalog.api.events.CreateWebhook; import org.openmetadata.catalog.jdbi3.WebhookRepository.WebhookEntityInterface; @@ -46,6 +45,7 @@ import org.openmetadata.catalog.type.FieldChange; import org.openmetadata.catalog.type.Webhook; import org.openmetadata.catalog.type.Webhook.Status; import org.openmetadata.catalog.util.EntityInterface; +import org.openmetadata.catalog.util.JsonUtils; import org.openmetadata.catalog.util.TestUtils; import org.openmetadata.catalog.util.TestUtils.UpdateType; @@ -65,57 +65,71 @@ public class WebhookResourceTest extends EntityResourceTest { @Test public void post_webhookEnabledStateChange() throws URISyntaxException, IOException, InterruptedException { - // Disabled webhook will not start webhook publisher + // + // Create webhook in disabled state. It will not start webhook publisher + // + LOG.info("creating webhook in disabled state"); webhookCallbackResource.resetCount(); String uri = "http://localhost:" + APP.getLocalPort() + "/api/v1/test/webhook/counter"; - CreateWebhook create = - createRequest("disabledWebhook", "", "", null).withEnabled(false).withEndpoint(URI.create(uri)); + CreateWebhook create = createRequest("counter", "", "", null).withEnabled(false).withEndpoint(URI.create(uri)); Webhook webhook = createAndCheckEntity(create, adminAuthHeaders()); assertEquals(Status.NOT_STARTED, webhook.getStatus()); Webhook getWebhook = getEntity(webhook.getId(), adminAuthHeaders()); assertEquals(Status.NOT_STARTED, getWebhook.getStatus()); assertEquals(0, webhookCallbackResource.getCount()); + // // Now enable the webhook + // + LOG.info("Enabling webhook"); int counter = webhookCallbackResource.getCount(); ChangeDescription change = getChangeDescription(webhook.getVersion()); change.getFieldsUpdated().add(new FieldChange().withName("enabled").withOldValue(false).withNewValue(true)); change .getFieldsUpdated() - .add(new FieldChange().withName("status").withOldValue(Status.NOT_STARTED).withNewValue(Status.SUCCESS)); - change - .getFieldsUpdated() - .add(new FieldChange().withName("batchSize").withOldValue(10).withNewValue(50)); + .add(new FieldChange().withName("status").withOldValue(Status.NOT_STARTED).withNewValue(Status.STARTED)); + change.getFieldsUpdated().add(new FieldChange().withName("batchSize").withOldValue(10).withNewValue(50)); create.withEnabled(true).withBatchSize(50); webhook = updateAndCheckEntity(create, Response.Status.OK, adminAuthHeaders(), UpdateType.MINOR_UPDATE, change); - assertEquals(Status.SUCCESS, webhook.getStatus()); + assertEquals(Status.STARTED, webhook.getStatus()); getWebhook = getEntity(webhook.getId(), adminAuthHeaders()); - assertEquals(Status.SUCCESS, getWebhook.getStatus()); + assertEquals(Status.STARTED, getWebhook.getStatus()); - // Change event for webhook enabling is received to ensure the call back has started + // Ensure the call back notification has started int iterations = 0; while (webhookCallbackResource.getCount() <= counter && iterations < 100) { Thread.sleep(10); iterations++; } assertEquals(counter + 1, webhookCallbackResource.getCount()); + long lastSuccessfulEventTime = webhookCallbackResource.getCounterLatestTime(); + FailureDetails failureDetails = new FailureDetails().withLastSuccessfulAt(lastSuccessfulEventTime); - // Disable the webhook and ensure it is disabled + // + // Disable the webhook and ensure notification is disabled + // + LOG.info("Disabling webhook"); create.withEnabled(false); - change = getChangeDescription(webhook.getVersion()); + change = getChangeDescription(getWebhook.getVersion()); + change + .getFieldsAdded() + .add(new FieldChange().withName("failureDetails").withNewValue(JsonUtils.pojoToJson(failureDetails))); change.getFieldsUpdated().add(new FieldChange().withName("enabled").withOldValue(true).withNewValue(false)); change .getFieldsUpdated() - .add(new FieldChange().withName("status").withOldValue(Status.SUCCESS).withNewValue(Status.NOT_STARTED)); + .add(new FieldChange().withName("status").withOldValue(Status.STARTED).withNewValue(Status.NOT_STARTED)); + // Disabled webhook state is NOT_STARTED getWebhook = updateAndCheckEntity(create, Response.Status.OK, adminAuthHeaders(), UpdateType.MINOR_UPDATE, change); assertEquals(Status.NOT_STARTED, getWebhook.getStatus()); + // Disabled webhook state also records last successful time when event was sent getWebhook = getEntity(webhook.getId(), adminAuthHeaders()); assertEquals(Status.NOT_STARTED, getWebhook.getStatus()); + assertEquals(webhookCallbackResource.getCounterStartTime(), getWebhook.getFailureDetails().getLastSuccessfulAt()); - // Ensure callback is disabled and no further events are received + // Ensure callback back notification is disabled with no new events iterations = 0; while (iterations < 100) { Thread.sleep(10); @@ -129,7 +143,7 @@ public class WebhookResourceTest extends EntityResourceTest { @Test public void put_updateEndpointURL() throws URISyntaxException, IOException, InterruptedException { CreateWebhook create = - createRequest("replaceURL", "", "", null).withEndpoint(URI.create("http://invalidUnknowHost")); + createRequest("counter", "", "", null).withEnabled(true).withEndpoint(URI.create("http://invalidUnknowHost")); Webhook webhook = createAndCheckEntity(create, adminAuthHeaders()); // Wait for webhook to be marked as failed @@ -149,7 +163,6 @@ public class WebhookResourceTest extends EntityResourceTest { String baseUri = "http://localhost:" + APP.getLocalPort() + "/api/v1/test/webhook/counter"; create = create.withEndpoint(URI.create(baseUri)); ChangeDescription change = getChangeDescription(getWebhook.getVersion()); - change.getFieldsDeleted().add(new FieldChange().withName("failureDetails").withOldValue(failureDetails)); change .getFieldsUpdated() .add( @@ -159,9 +172,8 @@ public class WebhookResourceTest extends EntityResourceTest { .withNewValue(create.getEndpoint())); change .getFieldsUpdated() - .add(new FieldChange().withName("status").withOldValue(Status.FAILED).withNewValue(Status.SUCCESS)); + .add(new FieldChange().withName("status").withOldValue(Status.FAILED).withNewValue(Status.STARTED)); webhook = updateAndCheckEntity(create, Response.Status.OK, adminAuthHeaders(), UpdateType.MINOR_UPDATE, change); - assertEquals(Status.SUCCESS, webhook.getStatus()); deleteEntity(webhook.getId(), adminAuthHeaders()); } @@ -175,7 +187,8 @@ public class WebhookResourceTest extends EntityResourceTest { .withDescription(description) .withEventFilters(ALL_EVENTS_FILTER) .withEndpoint(URI.create(uri)) - .withBatchSize(100); + .withBatchSize(100) + .withEnabled(false); } @Override @@ -218,9 +231,7 @@ public class WebhookResourceTest extends EntityResourceTest { public void startWebhookSubscription() throws IOException, URISyntaxException { // Valid webhook callback String baseUri = "http://localhost:" + APP.getLocalPort() + "/api/v1/test/webhook"; - CreateWebhook createWebhook = - createRequest("validWebhook", "validWebhook", "", null).withEndpoint(URI.create(baseUri)); - createEntity(createWebhook, adminAuthHeaders()); + createWebhook("validWebhook", baseUri); } /** Start webhook subscription for given entity and various event types */ @@ -323,13 +334,13 @@ public class WebhookResourceTest extends EntityResourceTest { public Webhook createWebhook(String name, String uri, List filters) throws URISyntaxException, IOException { CreateWebhook createWebhook = - createRequest(name, "", "", null).withEndpoint(URI.create(uri)).withEventFilters(filters); + createRequest(name, "", "", null).withEndpoint(URI.create(uri)).withEventFilters(filters).withEnabled(true); return createAndCheckEntity(createWebhook, adminAuthHeaders()); } public void assertWebhookStatusSuccess(String name) throws HttpResponseException { Webhook webhook = getEntityByName(name, "", adminAuthHeaders()); - assertEquals(Status.SUCCESS, webhook.getStatus()); + assertEquals(Status.STARTED, webhook.getStatus()); assertNull(webhook.getFailureDetails()); } diff --git a/openmetadata-ui/src/main/resources/ui/yarn.lock b/openmetadata-ui/src/main/resources/ui/yarn.lock index 93609e556d7..0aedae08ea0 100644 --- a/openmetadata-ui/src/main/resources/ui/yarn.lock +++ b/openmetadata-ui/src/main/resources/ui/yarn.lock @@ -3811,6 +3811,11 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= +compare-versions@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -5605,6 +5610,21 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-versions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-4.0.0.tgz#3c57e573bf97769b8cb8df16934b627915da4965" + integrity sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ== + dependencies: + semver-regex "^3.1.2" + flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" @@ -6345,6 +6365,22 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +husky@^4.3.0: + version "4.3.8" + resolved "https://registry.yarnpkg.com/husky/-/husky-4.3.8.tgz#31144060be963fd6850e5cc8f019a1dfe194296d" + integrity sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow== + dependencies: + chalk "^4.0.0" + ci-info "^2.0.0" + compare-versions "^3.6.0" + cosmiconfig "^7.0.0" + find-versions "^4.0.0" + opencollective-postinstall "^2.0.2" + pkg-dir "^5.0.0" + please-upgrade-node "^3.2.0" + slash "^3.0.0" + which-pm-runs "^1.0.0" + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -7913,6 +7949,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -8953,6 +8996,11 @@ openapi-sampler@1.0.0-beta.15: dependencies: json-pointer "^0.6.0" +opencollective-postinstall@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" @@ -9070,6 +9118,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" @@ -9330,6 +9385,13 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-dir@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" + integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== + dependencies: + find-up "^5.0.0" + please-upgrade-node@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" @@ -10700,6 +10762,11 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= +semver-regex@^3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.3.tgz#b2bcc6f97f63269f286994e297e229b6245d0dc3" + integrity sha512-Aqi54Mk9uYTjVexLnR67rTyBusmwd04cLkHy9hNvk3+G3nT2Oyg7E0l4XVbOaNwIvQ3hHeYxGcyEy+mKreyBFQ== + "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -12649,6 +12716,11 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-pm-runs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" + integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= + which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"