mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-16 19:28:21 +00:00
Fixes #1901 - Add support for storing last successful event timestamp to resume sending events on server shutdown (#1938)
This commit is contained in:
parent
5f479fb1c6
commit
4863e02a68
@ -520,7 +520,8 @@ public abstract class EntityRepository<T> {
|
||||
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<T> {
|
||||
return !newVersion.equals(oldVersion);
|
||||
}
|
||||
|
||||
private boolean fieldsChanged() {
|
||||
public boolean fieldsChanged() {
|
||||
return !changeDescription.getFieldsAdded().isEmpty()
|
||||
|| !changeDescription.getFieldsUpdated().isEmpty()
|
||||
|| !changeDescription.getFieldsDeleted().isEmpty();
|
||||
|
@ -119,7 +119,8 @@ public class WebhookRepository extends EntityRepository<Webhook> {
|
||||
}
|
||||
|
||||
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<Webhook> {
|
||||
}
|
||||
|
||||
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<Webhook> {
|
||||
// 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<ChangeEventHolder> processor = EventPubSub.addEventHandler(previousPublisher);
|
||||
previousPublisher.setProcessor(processor);
|
||||
@ -160,7 +161,7 @@ public class WebhookRepository extends EntityRepository<Webhook> {
|
||||
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<Webhook> {
|
||||
// 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<Webhook> {
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Webhook> putResponse = dao.createOrUpdate(uriInfo, webhook);
|
||||
dao.updateWebhookPublisher(webhook);
|
||||
return putResponse.toResponse();
|
||||
|
@ -109,9 +109,9 @@ public final class EntityUtil {
|
||||
public static final BiPredicate<MlFeature, MlFeature> mlFeatureMatch = MlFeature::equals;
|
||||
public static final BiPredicate<MlHyperParameter, MlHyperParameter> mlHyperParameterMatch = MlHyperParameter::equals;
|
||||
public static final BiPredicate<FailureDetails, FailureDetails> 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() {}
|
||||
|
||||
|
@ -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`",
|
||||
|
@ -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<ChangeEvent> changeEvents = new ConcurrentLinkedQueue<>();
|
||||
private final ConcurrentLinkedQueue<ChangeEvent> 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
|
||||
|
@ -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<Webhook> {
|
||||
|
||||
@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<Webhook> {
|
||||
@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<Webhook> {
|
||||
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<Webhook> {
|
||||
.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<Webhook> {
|
||||
.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<Webhook> {
|
||||
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<Webhook> {
|
||||
public Webhook createWebhook(String name, String uri, List<EventFilter> 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());
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user