From 2bc0f2c15e7a9181b9a09983ec2b1afcfdba4aec Mon Sep 17 00:00:00 2001 From: Joe Bell Date: Thu, 5 Jan 2023 14:56:57 -0800 Subject: [PATCH 001/170] refactor checks --- .../destination/s3/BlobStorageOperations.java | 4 +- .../destination/s3/S3BaseChecks.java | 2 +- .../destination/s3/S3StorageOperations.java | 10 +-- .../bigquery/BigQueryGcsOperations.java | 2 +- .../bigquery/BigQueryDestinationTest.java | 2 +- .../RedshiftStagingS3Destination.java | 74 ++++++++++--------- .../RedshiftS3StagingSqlOperations.java | 2 +- .../SnowflakeS3StagingSqlOperations.java | 2 +- 8 files changed, 46 insertions(+), 52 deletions(-) diff --git a/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/BlobStorageOperations.java b/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/BlobStorageOperations.java index d131470e976e..96681b4216f8 100644 --- a/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/BlobStorageOperations.java +++ b/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/BlobStorageOperations.java @@ -22,9 +22,9 @@ protected BlobStorageOperations() { public abstract String getBucketObjectPath(String namespace, String streamName, DateTime writeDatetime, String customFormat); /** - * Create a storage object where to store data in the destination for a @param objectPath + * Ensure that the bucket specified in the config exists */ - public abstract void createBucketObjectIfNotExists(String objectPath) throws Exception; + public abstract void createBucketIfNotExists() throws Exception; /** * Upload the data files into the storage area. diff --git a/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/S3BaseChecks.java b/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/S3BaseChecks.java index b5f2037e842b..0873cbb87a01 100644 --- a/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/S3BaseChecks.java +++ b/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/S3BaseChecks.java @@ -120,7 +120,7 @@ private static void attemptWriteAndDeleteS3Object(final S3StorageOperations stor final var bucketPath = s3Config.getBucketPath(); if (!Strings.isNullOrEmpty(bucketPath)) { - storageOperations.createBucketObjectIfNotExists(bucketPath); + storageOperations.createBucketIfNotExists(); } s3.putObject(s3Bucket, outputTableName, "check-content"); testIAMUserHasListObjectPermission(s3, s3Bucket); diff --git a/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/S3StorageOperations.java b/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/S3StorageOperations.java index 599463435142..fe0dfc519baa 100644 --- a/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/S3StorageOperations.java +++ b/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/S3StorageOperations.java @@ -95,23 +95,15 @@ public String getBucketObjectPath(final String namespace, final String streamNam /** * Create a directory object at the specified location. Creates the bucket if necessary. - * - * @param objectPath The directory to create. Must be a nonempty string. */ @Override - public void createBucketObjectIfNotExists(final String objectPath) { + public void createBucketIfNotExists() { final String bucket = s3Config.getBucketName(); - final String folderPath = objectPath.endsWith("/") ? objectPath : objectPath + "/"; if (!doesBucketExist(bucket)) { LOGGER.info("Bucket {} does not exist; creating...", bucket); s3Client.createBucket(bucket); LOGGER.info("Bucket {} has been created.", bucket); } - if (!s3Client.doesObjectExist(bucket, folderPath)) { - LOGGER.info("Storage Object {}/{} does not exist in bucket; creating...", bucket, objectPath); - s3Client.putObject(bucket, folderPath, ""); - LOGGER.info("Storage Object {}/{} has been created in bucket.", bucket, objectPath); - } } protected boolean doesBucketExist(final String bucket) { diff --git a/airbyte-integrations/connectors/destination-bigquery/src/main/java/io/airbyte/integrations/destination/bigquery/BigQueryGcsOperations.java b/airbyte-integrations/connectors/destination-bigquery/src/main/java/io/airbyte/integrations/destination/bigquery/BigQueryGcsOperations.java index 51a269725bef..5d9d16e7d3e1 100644 --- a/airbyte-integrations/connectors/destination-bigquery/src/main/java/io/airbyte/integrations/destination/bigquery/BigQueryGcsOperations.java +++ b/airbyte-integrations/connectors/destination-bigquery/src/main/java/io/airbyte/integrations/destination/bigquery/BigQueryGcsOperations.java @@ -99,7 +99,7 @@ public void createTmpTableIfNotExists(final TableId tmpTableId, final Schema tab public void createStageIfNotExists(final String datasetId, final String stream) { final String objectPath = getStagingFullPath(datasetId, stream); LOGGER.info("Creating staging path for stream {} (dataset {}): {}", stream, datasetId, objectPath); - gcsStorageOperations.createBucketObjectIfNotExists(objectPath); + gcsStorageOperations.createBucketIfNotExists(); } @Override diff --git a/airbyte-integrations/connectors/destination-bigquery/src/test-integration/java/io/airbyte/integrations/destination/bigquery/BigQueryDestinationTest.java b/airbyte-integrations/connectors/destination-bigquery/src/test-integration/java/io/airbyte/integrations/destination/bigquery/BigQueryDestinationTest.java index 8d93d74543d9..86e250a8c71d 100644 --- a/airbyte-integrations/connectors/destination-bigquery/src/test-integration/java/io/airbyte/integrations/destination/bigquery/BigQueryDestinationTest.java +++ b/airbyte-integrations/connectors/destination-bigquery/src/test-integration/java/io/airbyte/integrations/destination/bigquery/BigQueryDestinationTest.java @@ -81,7 +81,7 @@ class BigQueryDestinationTest { protected static final Path CREDENTIALS_WITH_GCS_STAGING_PATH = Path.of("secrets/credentials-gcs-staging.json"); - protected static final Path[] ALL_PATHS = {CREDENTIALS_WITH_GCS_STAGING_PATH, CREDENTIALS_BAD_PROJECT_PATH, CREDENTIALS_NO_DATASET_CREATION_PATH, + protected static final Path[] ALL_PATHS = {CREDENTIALS_STANDARD_INSERT_PATH, CREDENTIALS_BAD_PROJECT_PATH, CREDENTIALS_NO_DATASET_CREATION_PATH, CREDENTIALS_NON_BILLABLE_PROJECT_PATH, CREDENTIALS_WITH_GCS_STAGING_PATH}; private static final Logger LOGGER = LoggerFactory.getLogger(BigQueryDestinationTest.class); private static final String DATASET_NAME_PREFIX = "bq_dest_integration_test"; diff --git a/airbyte-integrations/connectors/destination-redshift/src/main/java/io/airbyte/integrations/destination/redshift/RedshiftStagingS3Destination.java b/airbyte-integrations/connectors/destination-redshift/src/main/java/io/airbyte/integrations/destination/redshift/RedshiftStagingS3Destination.java index 91a1b1983ccf..31b2c24944f8 100644 --- a/airbyte-integrations/connectors/destination-redshift/src/main/java/io/airbyte/integrations/destination/redshift/RedshiftStagingS3Destination.java +++ b/airbyte-integrations/connectors/destination-redshift/src/main/java/io/airbyte/integrations/destination/redshift/RedshiftStagingS3Destination.java @@ -58,46 +58,48 @@ private boolean isEphemeralKeysAndPurgingStagingData(final JsonNode config, fina @Override public AirbyteConnectionStatus check(final JsonNode config) { - final S3DestinationConfig s3Config = getS3DestinationConfig(findS3Options(config)); - final EncryptionConfig encryptionConfig = - config.has("uploading_method") ? EncryptionConfig.fromJson(config.get("uploading_method").get(JdbcUtils.ENCRYPTION_KEY)) : new NoEncryption(); - if (isEphemeralKeysAndPurgingStagingData(config, encryptionConfig)) { - return new AirbyteConnectionStatus() - .withStatus(Status.FAILED) - .withMessage( - "You cannot use ephemeral keys and disable purging your staging data. This would produce S3 objects that you cannot decrypt."); - } - S3BaseChecks.attemptS3WriteAndDelete(new S3StorageOperations(new RedshiftSQLNameTransformer(), s3Config.getS3Client(), s3Config), s3Config, - s3Config.getBucketPath()); - - final NamingConventionTransformer nameTransformer = getNamingResolver(); - final RedshiftS3StagingSqlOperations redshiftS3StagingSqlOperations = - new RedshiftS3StagingSqlOperations(nameTransformer, s3Config.getS3Client(), s3Config, encryptionConfig); - final DataSource dataSource = getDataSource(config); try { - final JdbcDatabase database = new DefaultJdbcDatabase(dataSource); - final String outputSchema = super.getNamingResolver().getIdentifier(config.get("schema").asText()); - attemptSQLCreateAndDropTableOperations(outputSchema, database, nameTransformer, redshiftS3StagingSqlOperations); - return new AirbyteConnectionStatus().withStatus(AirbyteConnectionStatus.Status.SUCCEEDED); - } catch (final ConnectionErrorException e) { - final String message = getErrorMessage(e.getStateCode(), e.getErrorCode(), e.getExceptionMessage(), e); - AirbyteTraceMessageUtility.emitConfigErrorTrace(e, message); - return new AirbyteConnectionStatus() - .withStatus(AirbyteConnectionStatus.Status.FAILED) - .withMessage(message); - } catch (final Exception e) { - LOGGER.error("Exception while checking connection: ", e); - return new AirbyteConnectionStatus() - .withStatus(AirbyteConnectionStatus.Status.FAILED) - .withMessage("Could not connect with provided configuration. \n" + e.getMessage()); - } finally { + final S3DestinationConfig s3Config = getS3DestinationConfig(findS3Options(config)); + final EncryptionConfig encryptionConfig = + config.has("uploading_method") ? EncryptionConfig.fromJson(config.get("uploading_method").get(JdbcUtils.ENCRYPTION_KEY)) : new NoEncryption(); + if (isEphemeralKeysAndPurgingStagingData(config, encryptionConfig)) { + return new AirbyteConnectionStatus() + .withStatus(Status.FAILED) + .withMessage( + "You cannot use ephemeral keys and disable purging your staging data. This would produce S3 objects that you cannot decrypt."); + } + S3BaseChecks.attemptS3WriteAndDelete(new S3StorageOperations(new RedshiftSQLNameTransformer(), s3Config.getS3Client(), s3Config), s3Config, + s3Config.getBucketPath()); + + final NamingConventionTransformer nameTransformer = getNamingResolver(); + final RedshiftS3StagingSqlOperations redshiftS3StagingSqlOperations = + new RedshiftS3StagingSqlOperations(nameTransformer, s3Config.getS3Client(), s3Config, encryptionConfig); + final DataSource dataSource = getDataSource(config); try { - DataSourceFactory.close(dataSource); - } catch (final Exception e) { - LOGGER.warn("Unable to close data source.", e); + final JdbcDatabase database = new DefaultJdbcDatabase(dataSource); + final String outputSchema = super.getNamingResolver().getIdentifier(config.get("schema").asText()); + attemptSQLCreateAndDropTableOperations(outputSchema, database, nameTransformer, redshiftS3StagingSqlOperations); + return new AirbyteConnectionStatus().withStatus(AirbyteConnectionStatus.Status.SUCCEEDED); + } catch (final ConnectionErrorException e) { + final String message = getErrorMessage(e.getStateCode(), e.getErrorCode(), e.getExceptionMessage(), e); + AirbyteTraceMessageUtility.emitConfigErrorTrace(e, message); + return new AirbyteConnectionStatus() + .withStatus(AirbyteConnectionStatus.Status.FAILED) + .withMessage(message); + } finally { + try { + DataSourceFactory.close(dataSource); + } catch (final Exception e) { + LOGGER.warn("Unable to close data source.", e); + } } - } + } catch (final Exception e) { + LOGGER.error("Exception while checking connection: ", e); + return new AirbyteConnectionStatus() + .withStatus(AirbyteConnectionStatus.Status.FAILED) + .withMessage("Could not connect with provided configuration. \n" + e.getMessage()); } +} @Override public DataSource getDataSource(final JsonNode config) { diff --git a/airbyte-integrations/connectors/destination-redshift/src/main/java/io/airbyte/integrations/destination/redshift/operations/RedshiftS3StagingSqlOperations.java b/airbyte-integrations/connectors/destination-redshift/src/main/java/io/airbyte/integrations/destination/redshift/operations/RedshiftS3StagingSqlOperations.java index 63abde3f6966..657a582d6eeb 100644 --- a/airbyte-integrations/connectors/destination-redshift/src/main/java/io/airbyte/integrations/destination/redshift/operations/RedshiftS3StagingSqlOperations.java +++ b/airbyte-integrations/connectors/destination-redshift/src/main/java/io/airbyte/integrations/destination/redshift/operations/RedshiftS3StagingSqlOperations.java @@ -77,7 +77,7 @@ public String getStagingPath(UUID connectionId, String namespace, String streamN public void createStageIfNotExists(JdbcDatabase database, String stageName) throws Exception { final String bucketPath = s3Config.getBucketPath(); final String prefix = bucketPath.isEmpty() ? "" : bucketPath + (bucketPath.endsWith("/") ? "" : "/"); - s3StorageOperations.createBucketObjectIfNotExists(prefix + stageName); + s3StorageOperations.createBucketIfNotExists(); } @Override diff --git a/airbyte-integrations/connectors/destination-snowflake/src/main/java/io/airbyte/integrations/destination/snowflake/SnowflakeS3StagingSqlOperations.java b/airbyte-integrations/connectors/destination-snowflake/src/main/java/io/airbyte/integrations/destination/snowflake/SnowflakeS3StagingSqlOperations.java index 9cdc3058be4e..e9c40de09116 100644 --- a/airbyte-integrations/connectors/destination-snowflake/src/main/java/io/airbyte/integrations/destination/snowflake/SnowflakeS3StagingSqlOperations.java +++ b/airbyte-integrations/connectors/destination-snowflake/src/main/java/io/airbyte/integrations/destination/snowflake/SnowflakeS3StagingSqlOperations.java @@ -82,7 +82,7 @@ public String uploadRecordsToStage(final JdbcDatabase database, @Override public void createStageIfNotExists(final JdbcDatabase database, final String stageName) { - s3StorageOperations.createBucketObjectIfNotExists(stageName); + s3StorageOperations.createBucketIfNotExists(); } @Override From f0038561c908a9ce8fa2031193168509c1c170e8 Mon Sep 17 00:00:00 2001 From: Joe Bell Date: Thu, 12 Jan 2023 12:37:11 -0800 Subject: [PATCH 002/170] Add Auth Error Checks --- .../destination/s3/S3StorageOperations.java | 10 +++++++++- .../destination/bigquery/BigQueryGcsOperations.java | 13 ++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/S3StorageOperations.java b/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/S3StorageOperations.java index fe0dfc519baa..4873786b9792 100644 --- a/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/S3StorageOperations.java +++ b/airbyte-integrations/bases/base-java-s3/src/main/java/io/airbyte/integrations/destination/s3/S3StorageOperations.java @@ -8,6 +8,7 @@ import alex.mojaki.s3upload.StreamTransferManager; import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.AmazonS3Exception; import com.amazonaws.services.s3.model.DeleteObjectsRequest; import com.amazonaws.services.s3.model.DeleteObjectsRequest.KeyVersion; import com.amazonaws.services.s3.model.ListObjectsRequest; @@ -15,6 +16,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; +import io.airbyte.commons.exceptions.ConfigErrorException; import io.airbyte.commons.string.Strings; import io.airbyte.integrations.destination.NamingConventionTransformer; import io.airbyte.integrations.destination.record_buffer.SerializableBuffer; @@ -130,7 +132,13 @@ public String uploadRecordsToBucket(final SerializableBuffer recordsData, exceptionsThrown.add(e); } } - throw new RuntimeException(String.format("Exceptions thrown while uploading records into storage: %s", Strings.join(exceptionsThrown, "\n"))); + final boolean areAllExceptionsAuthExceptions = exceptionsThrown.stream().filter(e -> e instanceof AmazonS3Exception) + .map(s3e -> ((AmazonS3Exception) s3e).getStatusCode()).allMatch(code -> Integer.valueOf(403).equals(code)); + if (areAllExceptionsAuthExceptions) { + throw new ConfigErrorException(exceptionsThrown.get(0).getMessage(), exceptionsThrown.get(0)); + } else { + throw new RuntimeException(String.format("Exceptions thrown while uploading records into storage: %s", Strings.join(exceptionsThrown, "\n"))); + } } /** diff --git a/airbyte-integrations/connectors/destination-bigquery/src/main/java/io/airbyte/integrations/destination/bigquery/BigQueryGcsOperations.java b/airbyte-integrations/connectors/destination-bigquery/src/main/java/io/airbyte/integrations/destination/bigquery/BigQueryGcsOperations.java index 5d9d16e7d3e1..5e55d319a273 100644 --- a/airbyte-integrations/connectors/destination-bigquery/src/main/java/io/airbyte/integrations/destination/bigquery/BigQueryGcsOperations.java +++ b/airbyte-integrations/connectors/destination-bigquery/src/main/java/io/airbyte/integrations/destination/bigquery/BigQueryGcsOperations.java @@ -13,6 +13,8 @@ import com.google.cloud.bigquery.LoadJobConfiguration; import com.google.cloud.bigquery.Schema; import com.google.cloud.bigquery.TableId; +import com.google.common.collect.ImmutableList; +import io.airbyte.commons.exceptions.ConfigErrorException; import io.airbyte.integrations.destination.StandardNameTransformer; import io.airbyte.integrations.destination.bigquery.uploader.AbstractBigQueryUploader; import io.airbyte.integrations.destination.gcs.GcsDestinationConfig; @@ -31,6 +33,7 @@ public class BigQueryGcsOperations implements BigQueryStagingOperations { private static final Logger LOGGER = LoggerFactory.getLogger(BigQueryGcsOperations.class); + private static final List AUTH_ERROR_CODES = ImmutableList.of(401, 403); private final BigQuery bigQuery; private final StandardNameTransformer gcsNameTransformer; private final GcsDestinationConfig gcsConfig; @@ -84,7 +87,15 @@ public String getStagingFullPath(final String datasetId, final String stream) { public void createSchemaIfNotExists(final String datasetId, final String datasetLocation) { if (!existingSchemas.contains(datasetId)) { LOGGER.info("Creating dataset {}", datasetId); - BigQueryUtils.getOrCreateDataset(bigQuery, datasetId, datasetLocation); + try { + BigQueryUtils.getOrCreateDataset(bigQuery, datasetId, datasetLocation); + } catch (BigQueryException bqe) { + if (AUTH_ERROR_CODES.contains(bqe.getCode())) { + throw new ConfigErrorException(bqe.getMessage(), bqe); + } else { + throw bqe; + } + } existingSchemas.add(datasetId); } } From 8130a40252e132d69ed06b075c4136d9e417588c Mon Sep 17 00:00:00 2001 From: Joe Bell Date: Thu, 12 Jan 2023 12:56:08 -0800 Subject: [PATCH 003/170] rebase from master --- .../destination-redshift/Dockerfile | 2 +- .../RedshiftStagingS3Destination.java | 74 +++++++++---------- docs/integrations/destinations/redshift.md | 1 - 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/airbyte-integrations/connectors/destination-redshift/Dockerfile b/airbyte-integrations/connectors/destination-redshift/Dockerfile index 5f06bd32cc8e..24a17530da92 100644 --- a/airbyte-integrations/connectors/destination-redshift/Dockerfile +++ b/airbyte-integrations/connectors/destination-redshift/Dockerfile @@ -16,5 +16,5 @@ ENV APPLICATION destination-redshift COPY --from=build /airbyte /airbyte -LABEL io.airbyte.version=0.3.53 +LABEL io.airbyte.version=0.3.52 LABEL io.airbyte.name=airbyte/destination-redshift diff --git a/airbyte-integrations/connectors/destination-redshift/src/main/java/io/airbyte/integrations/destination/redshift/RedshiftStagingS3Destination.java b/airbyte-integrations/connectors/destination-redshift/src/main/java/io/airbyte/integrations/destination/redshift/RedshiftStagingS3Destination.java index 8757b72e7084..f185235e4bf8 100644 --- a/airbyte-integrations/connectors/destination-redshift/src/main/java/io/airbyte/integrations/destination/redshift/RedshiftStagingS3Destination.java +++ b/airbyte-integrations/connectors/destination-redshift/src/main/java/io/airbyte/integrations/destination/redshift/RedshiftStagingS3Destination.java @@ -13,7 +13,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.google.common.annotations.VisibleForTesting; -import io.airbyte.commons.exceptions.ConfigErrorException; import io.airbyte.commons.exceptions.ConnectionErrorException; import io.airbyte.commons.json.Jsons; import io.airbyte.db.factory.DataSourceFactory; @@ -61,49 +60,44 @@ private boolean isEphemeralKeysAndPurgingStagingData(final JsonNode config, fina @Override public AirbyteConnectionStatus check(final JsonNode config) { - try { - final S3DestinationConfig s3Config = getS3DestinationConfig(findS3Options(config)); - final EncryptionConfig encryptionConfig = - config.has(UPLOADING_METHOD) ? EncryptionConfig.fromJson(config.get(UPLOADING_METHOD).get(JdbcUtils.ENCRYPTION_KEY)) : new NoEncryption(); - if (isEphemeralKeysAndPurgingStagingData(config, encryptionConfig)) { - return new AirbyteConnectionStatus() - .withStatus(Status.FAILED) - .withMessage( - "You cannot use ephemeral keys and disable purging your staging data. This would produce S3 objects that you cannot decrypt."); - } - S3BaseChecks.attemptS3WriteAndDelete(new S3StorageOperations(new RedshiftSQLNameTransformer(), s3Config.getS3Client(), s3Config), s3Config, - s3Config.getBucketPath()); + final S3DestinationConfig s3Config = getS3DestinationConfig(findS3Options(config)); + final EncryptionConfig encryptionConfig = + config.has(UPLOADING_METHOD) ? EncryptionConfig.fromJson(config.get(UPLOADING_METHOD).get(JdbcUtils.ENCRYPTION_KEY)) : new NoEncryption(); + if (isEphemeralKeysAndPurgingStagingData(config, encryptionConfig)) { + return new AirbyteConnectionStatus() + .withStatus(Status.FAILED) + .withMessage( + "You cannot use ephemeral keys and disable purging your staging data. This would produce S3 objects that you cannot decrypt."); + } + S3BaseChecks.attemptS3WriteAndDelete(new S3StorageOperations(new RedshiftSQLNameTransformer(), s3Config.getS3Client(), s3Config), s3Config, + s3Config.getBucketPath()); - final NamingConventionTransformer nameTransformer = getNamingResolver(); - final RedshiftS3StagingSqlOperations redshiftS3StagingSqlOperations = - new RedshiftS3StagingSqlOperations(nameTransformer, s3Config.getS3Client(), s3Config, encryptionConfig); - final DataSource dataSource = getDataSource(config); + final NamingConventionTransformer nameTransformer = getNamingResolver(); + final RedshiftS3StagingSqlOperations redshiftS3StagingSqlOperations = + new RedshiftS3StagingSqlOperations(nameTransformer, s3Config.getS3Client(), s3Config, encryptionConfig); + final DataSource dataSource = getDataSource(config); + try { + final JdbcDatabase database = new DefaultJdbcDatabase(dataSource); + final String outputSchema = super.getNamingResolver().getIdentifier(config.get(JdbcUtils.SCHEMA_KEY).asText()); + attemptSQLCreateAndDropTableOperations(outputSchema, database, nameTransformer, redshiftS3StagingSqlOperations); + return new AirbyteConnectionStatus().withStatus(AirbyteConnectionStatus.Status.SUCCEEDED); + } catch (final ConnectionErrorException e) { + final String message = getErrorMessage(e.getStateCode(), e.getErrorCode(), e.getExceptionMessage(), e); + AirbyteTraceMessageUtility.emitConfigErrorTrace(e, message); + return new AirbyteConnectionStatus() + .withStatus(AirbyteConnectionStatus.Status.FAILED) + .withMessage(message); + } catch (final Exception e) { + LOGGER.error("Exception while checking connection: ", e); + return new AirbyteConnectionStatus() + .withStatus(AirbyteConnectionStatus.Status.FAILED) + .withMessage("Could not connect with provided configuration. \n" + e.getMessage()); + } finally { try { - final JdbcDatabase database = new DefaultJdbcDatabase(dataSource); - final String outputSchema = super.getNamingResolver().getIdentifier(config.get(JdbcUtils.SCHEMA_KEY).asText()); - attemptSQLCreateAndDropTableOperations(outputSchema, database, nameTransformer, redshiftS3StagingSqlOperations); - return new AirbyteConnectionStatus().withStatus(AirbyteConnectionStatus.Status.SUCCEEDED); - } catch (final ConnectionErrorException e) { - final String message = getErrorMessage(e.getStateCode(), e.getErrorCode(), e.getExceptionMessage(), e); - AirbyteTraceMessageUtility.emitConfigErrorTrace(e, message); - return new AirbyteConnectionStatus() - .withStatus(AirbyteConnectionStatus.Status.FAILED) - .withMessage(message); + DataSourceFactory.close(dataSource); } catch (final Exception e) { - LOGGER.error("Exception while checking connection: ", e); - return new AirbyteConnectionStatus() - .withStatus(AirbyteConnectionStatus.Status.FAILED) - .withMessage("Could not connect with provided configuration. \n" + e.getMessage()); - } finally { - try { - DataSourceFactory.close(dataSource); - } catch (final Exception e) { - LOGGER.warn("Unable to close data source.", e); - } + LOGGER.warn("Unable to close data source.", e); } - } catch (final Exception e) { - LOGGER.error("Check failed.", e); - throw new ConfigErrorException(e.getMessage() != null ? e.getMessage() : e.toString()); } } diff --git a/docs/integrations/destinations/redshift.md b/docs/integrations/destinations/redshift.md index da21eb413b7c..2ffd20a4bc70 100644 --- a/docs/integrations/destinations/redshift.md +++ b/docs/integrations/destinations/redshift.md @@ -141,7 +141,6 @@ Each stream will be output into its own raw table in Redshift. Each table will c | Version | Date | Pull Request | Subject | |:--------|:-----------|:-----------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| 0.3.53 | 2023-01-03 | [\#17273](https://github.com/airbytehq/airbyte/pull/17273) | Fixed handling of arrays in SUPER maximum size check | | 0.3.52 | 2022-12-30 | [\#20879](https://github.com/airbytehq/airbyte/pull/20879) | Added configurable parameter for number of file buffers | | 0.3.51 | 2022-10-26 | [\#18434](https://github.com/airbytehq/airbyte/pull/18434) | Fix empty S3 bucket path handling | | 0.3.50 | 2022-09-14 | [\#15668](https://github.com/airbytehq/airbyte/pull/15668) | Wrap logs in AirbyteLogMessage | From 66a82019799a2f2cab464215c4a579f5261cd7d6 Mon Sep 17 00:00:00 2001 From: Joe Bell Date: Thu, 12 Jan 2023 13:59:14 -0800 Subject: [PATCH 004/170] some weird versioning issue --- .../connectors/destination-redshift/Dockerfile | 2 +- docs/integrations/destinations/redshift.md | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/airbyte-integrations/connectors/destination-redshift/Dockerfile b/airbyte-integrations/connectors/destination-redshift/Dockerfile index 24a17530da92..5f06bd32cc8e 100644 --- a/airbyte-integrations/connectors/destination-redshift/Dockerfile +++ b/airbyte-integrations/connectors/destination-redshift/Dockerfile @@ -16,5 +16,5 @@ ENV APPLICATION destination-redshift COPY --from=build /airbyte /airbyte -LABEL io.airbyte.version=0.3.52 +LABEL io.airbyte.version=0.3.53 LABEL io.airbyte.name=airbyte/destination-redshift diff --git a/docs/integrations/destinations/redshift.md b/docs/integrations/destinations/redshift.md index 2ffd20a4bc70..7a1bd583ac2f 100644 --- a/docs/integrations/destinations/redshift.md +++ b/docs/integrations/destinations/redshift.md @@ -141,6 +141,7 @@ Each stream will be output into its own raw table in Redshift. Each table will c | Version | Date | Pull Request | Subject | |:--------|:-----------|:-----------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 0.3.53 | 2023-01-03 | [\#17273](https://github.com/airbytehq/airbyte/pull/17273) | Fixed handling of arrays in SUPER maximum size check | | 0.3.52 | 2022-12-30 | [\#20879](https://github.com/airbytehq/airbyte/pull/20879) | Added configurable parameter for number of file buffers | | 0.3.51 | 2022-10-26 | [\#18434](https://github.com/airbytehq/airbyte/pull/18434) | Fix empty S3 bucket path handling | | 0.3.50 | 2022-09-14 | [\#15668](https://github.com/airbytehq/airbyte/pull/15668) | Wrap logs in AirbyteLogMessage | @@ -177,5 +178,3 @@ Each stream will be output into its own raw table in Redshift. Each table will c | 0.3.13 | 2021-09-02 | [5745](https://github.com/airbytehq/airbyte/pull/5745) | Disable STATUPDATE flag when using S3 staging to speed up performance | | 0.3.12 | 2021-07-21 | [3555](https://github.com/airbytehq/airbyte/pull/3555) | Enable partial checkpointing for halfway syncs | | 0.3.11 | 2021-07-20 | [4874](https://github.com/airbytehq/airbyte/pull/4874) | allow `additionalProperties` in connector spec | - - From d684daf1c975e891a6950a9a755f2756b18d1c9e Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 6 Jan 2023 00:01:48 +0100 Subject: [PATCH 005/170] =?UTF-8?q?=F0=9F=AA=9F=F0=9F=8E=89=20Connector=20?= =?UTF-8?q?builder:=20Substream=20slicer=20and=20cartesian=20slicer=20(#20?= =?UTF-8?q?861)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * improve some types * improve further * clean up a bit more * refactor loading state * move loading state up * remove isLoading references * remove unused props and make fetch connector error work * remove special component for name * remove top level state for unifinished flows * start removing uiwidget * Update airbyte-webapp/src/views/Connector/ConnectorCard/ConnectorCard.module.scss Co-authored-by: Tim Roes * remove undefined option for selected id * remove unused prop * fix types * remove uiwidget state * clean up * adjust comment * handle errors in a nice way * do not respect default on oneOf fields * rename to formblock * reduce re-renders * pass error to secure inputs * simplify and improve styling * align top * code review * remove comment * review comments * rename file * be strict about boolean values * add example * track form error in error boundary * review comments * handle unexpected cases better * enrich error with connector id * 🪟🎉 Add copy stream button (#20577) * add copy stream button * review comments * rename prop * 🪟🎉 Connector builder: Integrate connector form for test input (#20385) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * make connector form work in connector builder * fix small stuff * add warning label * review comments * adjust translation Co-authored-by: lmossman * use request_body_json instead of request_body_data * :window: :art: Move `Add` button into the line of Connector Builder key value list fields (#20699) * move add button into line * add stories for empty with control, and content + control * change button name to Control * 🪟🎉 Connector builder: Allow defining inputs (#20431) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * make connector form work in connector builder * wip * fix small stuff * add basic input UI * user inputs * make most of inputs configuration work * fix a bunch of stuff * handle unknown config types * add warning label * fix label * fix some styling * review comments * improve state management and error handling * handle stored form values that don't contain new fields properly * Update airbyte-webapp/src/locales/en.json Co-authored-by: Lake Mossman * Update airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx Co-authored-by: Lake Mossman * inputs editing weirdness * input form reset * using the Label component * 🪟🎉 Connector builder authentication (#20645) * allow auth configuration * check for conflicts with the inferred inputs * fix invisible inputs * reduce redundancy and hide advanced input options for inferred inputs * unnecessary validation * typo * unnecessary effect hook * build spec even for invalid forms but do not update stream list * fix keys * 🪟🎉 Connector builder: Session token and oauth authentication (#20712) * session token and oauth authentication * fill in session token variable * typos * make sure validation error does not go away * 🪟🎉 Connector builder: Always validate inputs form (#20664) * validate user input outside of form * review comments Co-authored-by: lmossman Co-authored-by: lmossman * fix merge conflict with dropdown prop being renamed to control * [Connector Builder] Add paginator (#20698) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * make connector form work in connector builder * wip * fix small stuff * add basic input UI * user inputs * make most of inputs configuration work * fix a bunch of stuff * handle unknown config types * add warning label * fix label * fix some styling * review comments * improve state management and error handling * allow auth configuration * check for conflicts with the inferred inputs * fix invisible inputs * handle stored form values that don't contain new fields properly * session token and oauth authentication * fill in session token variable * fix merge of default values * add primaryKey and cursorField to builder types, and consolidate default valeues to types.ts * add cursor and primary key fields to ui * save * add page size and token option inputs * fixes after rebase * add pagination * fix pagination types * handle empty field_name better * Update airbyte-webapp/src/locales/en.json Co-authored-by: Lake Mossman * Update airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx Co-authored-by: Lake Mossman * inputs editing weirdness * input form reset * using the Label component * reduce redundancy and hide advanced input options for inferred inputs * unnecessary validation * typo * unnecessary effect hook * build spec even for invalid forms but do not update stream list * typos * make sure validation error does not go away * make primary key and cursor optional, and reorder * save toggle group progress * fix style of toggle label * handle empty values better * fix page size/token option field validation and rendering * handle cursor pagination page size option correctly Co-authored-by: Joe Reuter * [Connector Builder] Add stream slicer (#20748) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * make connector form work in connector builder * wip * fix small stuff * add basic input UI * user inputs * make most of inputs configuration work * fix a bunch of stuff * handle unknown config types * add warning label * fix label * fix some styling * review comments * improve state management and error handling * allow auth configuration * check for conflicts with the inferred inputs * fix invisible inputs * handle stored form values that don't contain new fields properly * session token and oauth authentication * fill in session token variable * fix merge of default values * add primaryKey and cursorField to builder types, and consolidate default valeues to types.ts * add cursor and primary key fields to ui * save * add page size and token option inputs * fixes after rebase * add pagination * fix pagination types * handle empty field_name better * Update airbyte-webapp/src/locales/en.json Co-authored-by: Lake Mossman * Update airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx Co-authored-by: Lake Mossman * inputs editing weirdness * input form reset * using the Label component * reduce redundancy and hide advanced input options for inferred inputs * unnecessary validation * typo * unnecessary effect hook * build spec even for invalid forms but do not update stream list * typos * make sure validation error does not go away * make primary key and cursor optional, and reorder * save toggle group progress * fix style of toggle label * handle empty values better * fix page size/token option field validation and rendering * handle cursor pagination page size option correctly * save stream slicer progress * finish stream slicer * fix stream slicer fields and validation Co-authored-by: Joe Reuter * debounce form builder values update to reduce load * 🪟🔧 Connector builder: use new lowcode manifest (#20715) * use new manifest yaml * Update airbyte-webapp/src/components/connectorBuilder/types.ts Co-authored-by: Lake Mossman * use updated manifest types Co-authored-by: Lake Mossman * add substream slicer * add substream and cartesian slicer * debounce validation as well * akways show stream test button in error state if there are errors * fix type of oauth input * add validation schema for add stream form * validate all views on test click * add type to prevent console warning * do not allow path for substream slicer request option * do not show request option for substream slicer * rewrite stream slice field tooltip Co-authored-by: Tim Roes Co-authored-by: lmossman --- airbyte-webapp/package-lock.json | 41 ++- airbyte-webapp/package.json | 2 + .../Builder/AddStreamButton.tsx | 2 + .../Builder/BuilderList.module.scss | 13 + .../connectorBuilder/Builder/BuilderList.tsx | 55 +++ .../connectorBuilder/Builder/BuilderOneOf.tsx | 2 +- .../Builder/KeyValueListField.module.scss | 16 - .../Builder/KeyValueListField.tsx | 7 +- .../Builder/RemoveButton.module.scss | 20 ++ .../connectorBuilder/Builder/RemoveButton.tsx | 12 + .../Builder/StreamReferenceField.tsx | 58 ++++ .../Builder/StreamSlicerSection.tsx | 326 +++++++++++------- .../src/components/connectorBuilder/types.ts | 304 ++++++++++------ airbyte-webapp/src/locales/en.json | 1 + 14 files changed, 602 insertions(+), 257 deletions(-) create mode 100644 airbyte-webapp/src/components/connectorBuilder/Builder/BuilderList.module.scss create mode 100644 airbyte-webapp/src/components/connectorBuilder/Builder/BuilderList.tsx create mode 100644 airbyte-webapp/src/components/connectorBuilder/Builder/RemoveButton.module.scss create mode 100644 airbyte-webapp/src/components/connectorBuilder/Builder/RemoveButton.tsx create mode 100644 airbyte-webapp/src/components/connectorBuilder/Builder/StreamReferenceField.tsx diff --git a/airbyte-webapp/package-lock.json b/airbyte-webapp/package-lock.json index aa39b1fc6f0c..a100a69a9832 100644 --- a/airbyte-webapp/package-lock.json +++ b/airbyte-webapp/package-lock.json @@ -21,6 +21,7 @@ "@sentry/tracing": "^6.19.6", "@tanstack/react-table": "^8.7.0", "@types/segment-analytics": "^0.0.34", + "@types/uuid": "^9.0.0", "classnames": "^2.3.1", "dayjs": "^1.11.3", "firebase": "^9.8.2", @@ -61,6 +62,7 @@ "styled-components": "^5.3.5", "typesafe-actions": "^5.1.0", "unist-util-visit": "^4.1.0", + "uuid": "^9.0.0", "yup": "^0.32.11" }, "devDependencies": { @@ -14878,6 +14880,11 @@ "integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg==", "dev": true }, + "node_modules/@types/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==" + }, "node_modules/@types/webpack": { "version": "4.41.26", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.26.tgz", @@ -38581,6 +38588,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/react-scripts/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/react-scripts/node_modules/webpack-dev-middleware": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz", @@ -45241,10 +45257,9 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "bin": { "uuid": "dist/bin/uuid" } @@ -58236,6 +58251,11 @@ "integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg==", "dev": true }, + "@types/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==" + }, "@types/webpack": { "version": "4.41.26", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.26.tgz", @@ -76065,6 +76085,12 @@ "optional": true, "peer": true }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, "webpack-dev-middleware": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz", @@ -81084,10 +81110,9 @@ "dev": true }, "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" }, "uuid-browser": { "version": "3.1.0", diff --git a/airbyte-webapp/package.json b/airbyte-webapp/package.json index eb578d79b2bf..53fee68f7d79 100644 --- a/airbyte-webapp/package.json +++ b/airbyte-webapp/package.json @@ -40,6 +40,7 @@ "@sentry/tracing": "^6.19.6", "@tanstack/react-table": "^8.7.0", "@types/segment-analytics": "^0.0.34", + "@types/uuid": "^9.0.0", "classnames": "^2.3.1", "dayjs": "^1.11.3", "firebase": "^9.8.2", @@ -80,6 +81,7 @@ "styled-components": "^5.3.5", "typesafe-actions": "^5.1.0", "unist-util-visit": "^4.1.0", + "uuid": "^9.0.0", "yup": "^0.32.11" }, "devDependencies": { diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/AddStreamButton.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/AddStreamButton.tsx index 1933579f2fc4..cffa2cad32f8 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/AddStreamButton.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/AddStreamButton.tsx @@ -3,6 +3,7 @@ import merge from "lodash/merge"; import { useState } from "react"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; +import { v4 as uuid } from "uuid"; import * as yup from "yup"; import { Button } from "components/ui/Button"; @@ -55,6 +56,7 @@ export const AddStreamButton: React.FC = ({ onAddStream, b ...initialValues, name: values.streamName, urlPath: values.urlPath, + id: uuid(), }), ]); setIsOpen(false); diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderList.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderList.module.scss new file mode 100644 index 000000000000..dc1c5654cdc5 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderList.module.scss @@ -0,0 +1,13 @@ +@use "scss/variables"; +@use "scss/colors"; + +$removeButtonWidth: 20px; + +.itemWrapper { + display: flex; + gap: variables.$spacing-md; +} + +.itemContainer { + flex-grow: 1; +} diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderList.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderList.tsx new file mode 100644 index 000000000000..60d3d3067e10 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderList.tsx @@ -0,0 +1,55 @@ +import { faPlus } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useField } from "formik"; +import React, { ReactElement, useMemo } from "react"; +import { FormattedMessage } from "react-intl"; + +import { Button } from "components/ui/Button"; + +import styles from "./BuilderList.module.scss"; +import { RemoveButton } from "./RemoveButton"; + +interface BuilderListProps { + children: (props: { buildPath: (path: string) => string }) => ReactElement; + basePath: string; + emptyItem: object; +} + +export const BuilderList: React.FC = ({ children, emptyItem, basePath }) => { + const [list, , helpers] = useField(basePath); + + const buildPathFunctions = useMemo( + () => + new Array(list.value.length).fill(undefined).map((_value, index) => { + return (path: string) => `${basePath}[${index}]${path !== "" ? "." : ""}${path}`; + }), + [basePath, list.value.length] + ); + + return ( + <> + {buildPathFunctions.map((buildPath, currentItemIndex) => ( +
+
{children({ buildPath })}
+ { + const updatedItems = list.value.filter((_, index) => index !== currentItemIndex); + helpers.setValue(updatedItems); + }} + /> +
+ ))} +
+ +
+ + ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderOneOf.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderOneOf.tsx index 2fc53cb59e28..e6bec6bbbbcd 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderOneOf.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderOneOf.tsx @@ -11,7 +11,7 @@ interface Option { default?: object; } -interface OneOfOption { +export interface OneOfOption { label: string; // label shown in the dropdown menu typeValue: string; // value to set on the `type` field for this component - should match the oneOf type definition default?: object; // default values for the path diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.module.scss index 62a4b592138c..daba1c15331c 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.module.scss +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.module.scss @@ -21,19 +21,3 @@ $removeButtonWidth: 20px; .kvLabel { color: colors.$grey-400; } - -.removeButton { - border: none; - background-color: transparent; - color: colors.$grey; - cursor: pointer; - padding: 0; - width: $removeButtonWidth; - height: $removeButtonWidth; - font-size: 18px; - transition: variables.$transition; - - &:hover { - color: colors.$red; - } -} diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.tsx index 487534424234..14b2f5fe0817 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.tsx @@ -1,5 +1,3 @@ -import { faXmark } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useField } from "formik"; import { FormattedMessage } from "react-intl"; @@ -10,6 +8,7 @@ import { Input } from "components/ui/Input"; import { Text } from "components/ui/Text"; import styles from "./KeyValueListField.module.scss"; +import { RemoveButton } from "./RemoveButton"; interface KeyValueInputProps { keyValue: [string, string]; @@ -32,9 +31,7 @@ const KeyValueInput: React.FC = ({ keyValue, onChange, onRem onChange([keyValue[0], e.target.value])} /> - + ); }; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/RemoveButton.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/RemoveButton.module.scss new file mode 100644 index 000000000000..21074ab29463 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/RemoveButton.module.scss @@ -0,0 +1,20 @@ +@use "scss/variables"; +@use "scss/colors"; + +$removeButtonWidth: 20px; + +.removeButton { + border: none; + background-color: transparent; + color: colors.$grey; + cursor: pointer; + padding: 0; + width: $removeButtonWidth; + height: $removeButtonWidth; + font-size: 18px; + transition: variables.$transition; + + &:hover { + color: colors.$red; + } +} diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/RemoveButton.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/RemoveButton.tsx new file mode 100644 index 000000000000..50326541ef61 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/RemoveButton.tsx @@ -0,0 +1,12 @@ +import { faXmark } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import styles from "./RemoveButton.module.scss"; + +export const RemoveButton = ({ onClick }: { onClick: () => void }) => { + return ( + + ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/StreamReferenceField.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/StreamReferenceField.tsx new file mode 100644 index 000000000000..a5d650efe64e --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/StreamReferenceField.tsx @@ -0,0 +1,58 @@ +import { useField } from "formik"; +import { useMemo } from "react"; +import { FormattedMessage } from "react-intl"; + +import { ControlLabels } from "components/LabeledControl"; +import { DropDown } from "components/ui/DropDown"; +import { Text } from "components/ui/Text"; + +import { BuilderStream } from "../types"; +import styles from "./BuilderField.module.scss"; + +interface StreamReferenceFieldProps { + // path to the location in the Connector Manifest schema which should be set by this component + path: string; + label: string; + tooltip?: string; + optional?: boolean; + currentStreamIndex: number; +} + +export const StreamReferenceField: React.FC = ({ + path, + label, + tooltip, + optional, + currentStreamIndex, + ...props +}) => { + const [streams] = useField("streams"); + const [field, meta, helpers] = useField(path); + const hasError = !!meta.error && meta.touched; + + const options = useMemo(() => { + return streams.value + .filter((_value, index) => index !== currentStreamIndex) + .map((stream) => ({ + value: stream.id, + label: stream.name, + })); + }, [currentStreamIndex, streams.value]); + + return ( + + selected && helpers.setValue(selected.value)} + value={field.value} + error={hasError} + /> + {hasError && ( + + + + )} + + ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/StreamSlicerSection.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/StreamSlicerSection.tsx index 514330b89b56..490133d6f881 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/StreamSlicerSection.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/StreamSlicerSection.tsx @@ -8,9 +8,11 @@ import { RequestOption, SimpleRetrieverStreamSlicer } from "core/request/Connect import { timeDeltaRegex } from "../types"; import { BuilderCard } from "./BuilderCard"; import { BuilderField } from "./BuilderField"; -import { BuilderOneOf } from "./BuilderOneOf"; +import { BuilderList } from "./BuilderList"; +import { BuilderOneOf, OneOfOption } from "./BuilderOneOf"; import { BuilderOptional } from "./BuilderOptional"; import { InjectRequestOptionFields } from "./InjectRequestOptionFields"; +import { StreamReferenceField } from "./StreamReferenceField"; import { ToggleGroupField } from "./ToggleGroupField"; interface StreamSlicerSectionProps { @@ -35,6 +37,181 @@ export const StreamSlicerSection: React.FC = ({ stream }; const toggledOn = field.value !== undefined; + const getRegularSlicingOptions = (buildPath: (path: string) => string): OneOfOption[] => [ + { + label: "List", + typeValue: "ListStreamSlicer", + default: { + slice_values: [], + cursor_field: "", + }, + children: ( + <> + + + + label="Slice request option" + tooltip="Optionally configures how the slice values will be sent in requests to the source API" + fieldPath={buildPath("request_option")} + initialValues={{ + inject_into: "request_parameter", + type: "RequestOption", + field_name: "", + }} + > + + + + ), + }, + { + label: "Datetime", + typeValue: "DatetimeStreamSlicer", + default: { + datetime_format: "", + start_datetime: "", + end_datetime: "", + step: "", + cursor_field: "", + }, + children: ( + <> + + + + + + + + + label="Start time request option" + tooltip="Optionally configures how the start datetime will be sent in requests to the source API" + fieldPath={buildPath("start_time_option")} + initialValues={{ + inject_into: "request_parameter", + type: "RequestOption", + field_name: "", + }} + > + + + + label="End time request option" + tooltip="Optionally configures how the end datetime will be sent in requests to the source API" + fieldPath={buildPath("end_time_option")} + initialValues={{ + inject_into: "request_parameter", + type: "RequestOption", + field_name: "", + }} + > + + + + + + + ), + }, + { + label: "Substream", + typeValue: "SubstreamSlicer", + default: { + parent_key: "", + stream_slice_field: "", + parentStreamReference: "", + }, + children: ( + <> + + + + + ), + }, + ]; + return ( = ({ stream label="Mode" tooltip="Stream slicer method to use on this stream" options={[ + ...getRegularSlicingOptions((path: string) => streamFieldPath(`streamSlicer.${path}`)), { - label: "List", - typeValue: "ListStreamSlicer", - children: ( - <> - - - - label="Slice request option" - tooltip="Optionally configures how the slice values will be sent in requests to the source API" - fieldPath={streamFieldPath("streamSlicer.request_option")} - initialValues={{ - inject_into: "request_parameter", - type: "RequestOption", - field_name: "", - }} - > - - - - ), - }, - { - label: "Datetime", - typeValue: "DatetimeStreamSlicer", + label: "Cartesian product", + typeValue: "CartesianProductStreamSlicer", + default: { + stream_slicers: [], + }, children: ( - <> - - - - - - - - - label="Start time request option" - tooltip="Optionally configures how the start datetime will be sent in requests to the source API" - fieldPath={streamFieldPath("streamSlicer.start_time_option")} - initialValues={{ - inject_into: "request_parameter", - type: "RequestOption", - field_name: "", - }} - > - - - - label="End time request option" - tooltip="Optionally configures how the end datetime will be sent in requests to the source API" - fieldPath={streamFieldPath("streamSlicer.end_time_option")} - initialValues={{ - inject_into: "request_parameter", - type: "RequestOption", - field_name: "", - }} - > - - - - + {({ buildPath }) => ( + - - + )} + ), }, ]} diff --git a/airbyte-webapp/src/components/connectorBuilder/types.ts b/airbyte-webapp/src/components/connectorBuilder/types.ts index bc7c00db4564..6f60eac5cfcd 100644 --- a/airbyte-webapp/src/components/connectorBuilder/types.ts +++ b/airbyte-webapp/src/components/connectorBuilder/types.ts @@ -17,6 +17,9 @@ import { DefaultPaginatorPaginationStrategy, SimpleRetrieverStreamSlicer, HttpRequesterAuthenticator, + SubstreamSlicer, + SubstreamSlicerType, + CartesianProductStreamSlicer, } from "core/request/ConnectorManifest"; export interface BuilderFormInput { @@ -53,7 +56,23 @@ export interface BuilderPaginator { pageSizeOption?: RequestOption; } +export interface BuilderSubstreamSlicer { + type: SubstreamSlicerType; + parent_key: string; + stream_slice_field: string; + parentStreamReference: string; + request_option?: RequestOption; +} + +export interface BuilderCartesianProductSlicer { + type: "CartesianProductStreamSlicer"; + stream_slicers: Array< + Exclude | BuilderSubstreamSlicer + >; +} + export interface BuilderStream { + id: string; name: string; urlPath: string; fieldPointer: string[]; @@ -65,7 +84,10 @@ export interface BuilderStream { requestBody: Array<[string, string]>; }; paginator?: BuilderPaginator; - streamSlicer?: SimpleRetrieverStreamSlicer; + streamSlicer?: + | Exclude + | BuilderSubstreamSlicer + | BuilderCartesianProductSlicer; schema?: string; } @@ -80,7 +102,7 @@ export const DEFAULT_BUILDER_FORM_VALUES: BuilderFormValues = { streams: [], }; -export const DEFAULT_BUILDER_STREAM_VALUES: BuilderStream = { +export const DEFAULT_BUILDER_STREAM_VALUES: Omit = { name: "", urlPath: "", fieldPointer: [], @@ -231,6 +253,80 @@ const nonPathRequestOptionSchema = yup // eslint-disable-next-line no-useless-escape export const timeDeltaRegex = /^(([\.\d]+?)y)?(([\.\d]+?)m)?(([\.\d]+?)w)?(([\.\d]+?)d)?$/; +const regularSlicerShape = { + cursor_field: yup.mixed().when("type", { + is: (val: string) => val !== "SubstreamSlicer" && val !== "CartesianProductStreamSlicer", + then: yup.string().required("form.empty.error"), + otherwise: (schema) => schema.strip(), + }), + slice_values: yup.mixed().when("type", { + is: "ListStreamSlicer", + then: yup.array().of(yup.string()), + otherwise: (schema) => schema.strip(), + }), + request_option: nonPathRequestOptionSchema, + start_datetime: yup.mixed().when("type", { + is: "DatetimeStreamSlicer", + then: yup.string().required("form.empty.error"), + otherwise: (schema) => schema.strip(), + }), + end_datetime: yup.mixed().when("type", { + is: "DatetimeStreamSlicer", + then: yup.string().required("form.empty.error"), + otherwise: (schema) => schema.strip(), + }), + step: yup.mixed().when("type", { + is: "DatetimeStreamSlicer", + then: yup.string().matches(timeDeltaRegex, "form.pattern.error").required("form.empty.error"), + otherwise: (schema) => schema.strip(), + }), + datetime_format: yup.mixed().when("type", { + is: "DatetimeStreamSlicer", + then: yup.string().required("form.empty.error"), + otherwise: (schema) => schema.strip(), + }), + start_time_option: yup.mixed().when("type", { + is: "DatetimeStreamSlicer", + then: nonPathRequestOptionSchema, + otherwise: (schema) => schema.strip(), + }), + end_time_option: yup.mixed().when("type", { + is: "DatetimeStreamSlicer", + then: nonPathRequestOptionSchema, + otherwise: (schema) => schema.strip(), + }), + stream_state_field_start: yup.mixed().when("type", { + is: "DatetimeStreamSlicer", + then: yup.string(), + otherwise: (schema) => schema.strip(), + }), + stream_state_field_end: yup.mixed().when("type", { + is: "DatetimeStreamSlicer", + then: yup.string(), + otherwise: (schema) => schema.strip(), + }), + lookback_window: yup.mixed().when("type", { + is: "DatetimeStreamSlicer", + then: yup.string(), + otherwise: (schema) => schema.strip(), + }), + parent_key: yup.mixed().when("type", { + is: "SubstreamSlicer", + then: yup.string().required("form.empty.error"), + otherwise: (schema) => schema.strip(), + }), + parentStreamReference: yup.mixed().when("type", { + is: "SubstreamSlicer", + then: yup.string().required("form.empty.error"), + otherwise: (schema) => schema.strip(), + }), + stream_slice_field: yup.mixed().when("type", { + is: "SubstreamSlicer", + then: yup.string().required("form.empty.error"), + otherwise: (schema) => schema.strip(), + }), +}; + export const builderFormValidationSchema = yup.object().shape({ global: yup.object().shape({ connectorName: yup.string().required("form.empty.error"), @@ -332,56 +428,10 @@ export const builderFormValidationSchema = yup.object().shape({ streamSlicer: yup .object() .shape({ - cursor_field: yup.string().required("form.empty.error"), - slice_values: yup.mixed().when("type", { - is: "ListStreamSlicer", - then: yup.array().of(yup.string()), - otherwise: (schema) => schema.strip(), - }), - request_option: nonPathRequestOptionSchema, - start_datetime: yup.mixed().when("type", { - is: "DatetimeStreamSlicer", - then: yup.string().required("form.empty.error"), - otherwise: (schema) => schema.strip(), - }), - end_datetime: yup.mixed().when("type", { - is: "DatetimeStreamSlicer", - then: yup.string().required("form.empty.error"), - otherwise: (schema) => schema.strip(), - }), - step: yup.mixed().when("type", { - is: "DatetimeStreamSlicer", - then: yup.string().matches(timeDeltaRegex, "form.pattern.error").required("form.empty.error"), - otherwise: (schema) => schema.strip(), - }), - datetime_format: yup.mixed().when("type", { - is: "DatetimeStreamSlicer", - then: yup.string().required("form.empty.error"), - otherwise: (schema) => schema.strip(), - }), - start_time_option: yup.mixed().when("type", { - is: "DatetimeStreamSlicer", - then: nonPathRequestOptionSchema, - otherwise: (schema) => schema.strip(), - }), - end_time_option: yup.mixed().when("type", { - is: "DatetimeStreamSlicer", - then: nonPathRequestOptionSchema, - otherwise: (schema) => schema.strip(), - }), - stream_state_field_start: yup.mixed().when("type", { - is: "DatetimeStreamSlicer", - then: yup.string(), - otherwise: (schema) => schema.strip(), - }), - stream_state_field_end: yup.mixed().when("type", { - is: "DatetimeStreamSlicer", - then: yup.string(), - otherwise: (schema) => schema.strip(), - }), - lookback_window: yup.mixed().when("type", { - is: "DatetimeStreamSlicer", - then: yup.string(), + ...regularSlicerShape, + stream_slicers: yup.mixed().when("type", { + is: "CartesianProductStreamSlicer", + then: yup.array().of(yup.object().shape(regularSlicerShape)), otherwise: (schema) => schema.strip(), }), }) @@ -409,6 +459,53 @@ function builderFormAuthenticatorToAuthenticator( return globalSettings.authenticator as HttpRequesterAuthenticator; } +function builderFormStreamSlicerToStreamSlicer( + values: BuilderFormValues, + slicer: BuilderStream["streamSlicer"], + visitedStreams: string[] +): SimpleRetrieverStreamSlicer | undefined { + if (!slicer) { + return undefined; + } + if (slicer.type !== "SubstreamSlicer" && slicer.type !== "CartesianProductStreamSlicer") { + return slicer; + } + if (slicer.type === "CartesianProductStreamSlicer") { + return { + type: "CartesianProductStreamSlicer", + stream_slicers: slicer.stream_slicers.map((subSlicer) => { + return builderFormStreamSlicerToStreamSlicer(values, subSlicer, visitedStreams); + }), + } as unknown as CartesianProductStreamSlicer; + } + const parentStream = values.streams.find(({ id }) => id === slicer.parentStreamReference); + if (!parentStream) { + return { + type: "SubstreamSlicer", + parent_stream_configs: [], + }; + } + if (visitedStreams.includes(parentStream.id)) { + // circular dependency + return { + type: "SubstreamSlicer", + parent_stream_configs: [], + }; + } + return { + type: "SubstreamSlicer", + parent_stream_configs: [ + { + type: "ParentStreamConfig", + parent_key: slicer.parent_key, + request_option: slicer.request_option, + stream_slice_field: slicer.stream_slice_field, + stream: builderStreamToDeclarativeSteam(values, parentStream, visitedStreams), + }, + ], + }; +} + function parseSchemaString(schema?: string) { if (!schema) { return undefined; @@ -420,60 +517,65 @@ function parseSchemaString(schema?: string) { } } -export const convertToManifest = (values: BuilderFormValues): ConnectorManifest => { - const manifestStreams: DeclarativeStream[] = values.streams.map((stream) => { - return { - type: "DeclarativeStream", +function builderStreamToDeclarativeSteam( + values: BuilderFormValues, + stream: BuilderStream, + visitedStreams: string[] +): DeclarativeStream { + return { + type: "DeclarativeStream", + name: stream.name, + primary_key: stream.primaryKey, + schema_loader: parseSchemaString(stream.schema), + retriever: { + type: "SimpleRetriever", name: stream.name, primary_key: stream.primaryKey, - schema_loader: parseSchemaString(stream.schema), - retriever: { - type: "SimpleRetriever", + requester: { + type: "HttpRequester", name: stream.name, - primary_key: stream.primaryKey, - requester: { - type: "HttpRequester", - name: stream.name, - url_base: values.global?.urlBase, - path: stream.urlPath, - request_options_provider: { - // TODO can't declare type here because the server will error out, but the types dictate it is needed. Fix here once server is fixed. - // type: "InterpolatedRequestOptionsProvider", - request_parameters: Object.fromEntries(stream.requestOptions.requestParameters), - request_headers: Object.fromEntries(stream.requestOptions.requestHeaders), - request_body_json: Object.fromEntries(stream.requestOptions.requestBody), - } as InterpolatedRequestOptionsProvider, - authenticator: builderFormAuthenticatorToAuthenticator(values.global), - // TODO: remove these empty "config" values once they are no longer required in the connector manifest JSON schema - config: {}, - }, - record_selector: { - type: "RecordSelector", - extractor: { - type: "DpathExtractor", - field_pointer: stream.fieldPointer, - }, + url_base: values.global?.urlBase, + path: stream.urlPath, + request_options_provider: { + // TODO can't declare type here because the server will error out, but the types dictate it is needed. Fix here once server is fixed. + // type: "InterpolatedRequestOptionsProvider", + request_parameters: Object.fromEntries(stream.requestOptions.requestParameters), + request_headers: Object.fromEntries(stream.requestOptions.requestHeaders), + request_body_json: Object.fromEntries(stream.requestOptions.requestBody), + } as InterpolatedRequestOptionsProvider, + authenticator: builderFormAuthenticatorToAuthenticator(values.global), + }, + record_selector: { + type: "RecordSelector", + extractor: { + type: "DpathExtractor", + field_pointer: stream.fieldPointer, }, - paginator: stream.paginator - ? { - type: "DefaultPaginator", - page_token_option: { - ...stream.paginator.pageTokenOption, - // ensures that empty field_name is not set, as connector builder server cannot accept a field_name if inject_into is set to 'path' - field_name: stream.paginator.pageTokenOption?.field_name - ? stream.paginator.pageTokenOption?.field_name - : undefined, - }, - page_size_option: stream.paginator.pageSizeOption, - pagination_strategy: stream.paginator.strategy, - url_base: values.global?.urlBase, - } - : { type: "NoPagination" }, - stream_slicer: stream.streamSlicer, - config: {}, }, - }; - }); + paginator: stream.paginator + ? { + type: "DefaultPaginator", + page_token_option: { + ...stream.paginator.pageTokenOption, + // ensures that empty field_name is not set, as connector builder server cannot accept a field_name if inject_into is set to 'path' + field_name: stream.paginator.pageTokenOption?.field_name + ? stream.paginator.pageTokenOption?.field_name + : undefined, + }, + page_size_option: stream.paginator.pageSizeOption, + pagination_strategy: stream.paginator.strategy, + url_base: values.global?.urlBase, + } + : { type: "NoPagination" }, + stream_slicer: builderFormStreamSlicerToStreamSlicer(values, stream.streamSlicer, [...visitedStreams, stream.id]), + }, + }; +} + +export const convertToManifest = (values: BuilderFormValues): ConnectorManifest => { + const manifestStreams: DeclarativeStream[] = values.streams.map((stream) => + builderStreamToDeclarativeSteam(values, stream, []) + ); const allInputs = [...values.inputs, ...getInferredInputs(values)]; diff --git a/airbyte-webapp/src/locales/en.json b/airbyte-webapp/src/locales/en.json index 16fcf517dda0..38d4c8e33638 100644 --- a/airbyte-webapp/src/locales/en.json +++ b/airbyte-webapp/src/locales/en.json @@ -718,6 +718,7 @@ "connectorBuilder.setInUserInput": "This setting is configured as part of the user inputs in the testing panel", "connectorBuilder.optionalFieldsLabel": "Optional fields", "connectorBuilder.duplicateFieldID": "Make sure no field ID is used multiple times", + "connectorBuilder.addNewSlicer": "Add new slicer", "connectorBuilder.streamConfiguration": "Configuration", "connectorBuilder.streamSchema": "Schema", "connectorBuilder.invalidSchema": "Invalid JSON - please fix schema to have it applied", From fc662093b73633e66ec33945a865ac5c05b816ee Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 6 Jan 2023 00:15:10 +0100 Subject: [PATCH 006/170] =?UTF-8?q?=F0=9F=AA=9F=F0=9F=94=A7=20Connector=20?= =?UTF-8?q?builder:=20Performance=20improvements=20(#20620)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * improve some types * improve further * clean up a bit more * refactor loading state * move loading state up * remove isLoading references * remove unused props and make fetch connector error work * remove special component for name * remove top level state for unifinished flows * start removing uiwidget * Update airbyte-webapp/src/views/Connector/ConnectorCard/ConnectorCard.module.scss Co-authored-by: Tim Roes * remove undefined option for selected id * remove unused prop * fix types * remove uiwidget state * clean up * adjust comment * handle errors in a nice way * do not respect default on oneOf fields * rename to formblock * reduce re-renders * pass error to secure inputs * simplify and improve styling * align top * code review * remove comment * review comments * rename file * be strict about boolean values * add example * track form error in error boundary * review comments * handle unexpected cases better * speed up some bits * more changes * enrich error with connector id * 🪟🎉 Add copy stream button (#20577) * add copy stream button * review comments * rename prop * 🪟🎉 Connector builder: Integrate connector form for test input (#20385) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * make connector form work in connector builder * fix small stuff * add warning label * review comments * adjust translation Co-authored-by: lmossman * use request_body_json instead of request_body_data * :window: :art: Move `Add` button into the line of Connector Builder key value list fields (#20699) * move add button into line * add stories for empty with control, and content + control * change button name to Control * 🪟🎉 Connector builder: Allow defining inputs (#20431) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * make connector form work in connector builder * wip * fix small stuff * add basic input UI * user inputs * make most of inputs configuration work * fix a bunch of stuff * handle unknown config types * add warning label * fix label * fix some styling * review comments * improve state management and error handling * handle stored form values that don't contain new fields properly * Update airbyte-webapp/src/locales/en.json Co-authored-by: Lake Mossman * Update airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx Co-authored-by: Lake Mossman * inputs editing weirdness * input form reset * using the Label component * 🪟🎉 Connector builder authentication (#20645) * allow auth configuration * check for conflicts with the inferred inputs * fix invisible inputs * reduce redundancy and hide advanced input options for inferred inputs * unnecessary validation * typo * unnecessary effect hook * build spec even for invalid forms but do not update stream list * fix keys * 🪟🎉 Connector builder: Session token and oauth authentication (#20712) * session token and oauth authentication * fill in session token variable * typos * make sure validation error does not go away * 🪟🎉 Connector builder: Always validate inputs form (#20664) * validate user input outside of form * review comments Co-authored-by: lmossman Co-authored-by: lmossman * fix merge conflict with dropdown prop being renamed to control * [Connector Builder] Add paginator (#20698) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * make connector form work in connector builder * wip * fix small stuff * add basic input UI * user inputs * make most of inputs configuration work * fix a bunch of stuff * handle unknown config types * add warning label * fix label * fix some styling * review comments * improve state management and error handling * allow auth configuration * check for conflicts with the inferred inputs * fix invisible inputs * handle stored form values that don't contain new fields properly * session token and oauth authentication * fill in session token variable * fix merge of default values * add primaryKey and cursorField to builder types, and consolidate default valeues to types.ts * add cursor and primary key fields to ui * save * add page size and token option inputs * fixes after rebase * add pagination * fix pagination types * handle empty field_name better * Update airbyte-webapp/src/locales/en.json Co-authored-by: Lake Mossman * Update airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx Co-authored-by: Lake Mossman * inputs editing weirdness * input form reset * using the Label component * reduce redundancy and hide advanced input options for inferred inputs * unnecessary validation * typo * unnecessary effect hook * build spec even for invalid forms but do not update stream list * typos * make sure validation error does not go away * make primary key and cursor optional, and reorder * save toggle group progress * fix style of toggle label * handle empty values better * fix page size/token option field validation and rendering * handle cursor pagination page size option correctly Co-authored-by: Joe Reuter * [Connector Builder] Add stream slicer (#20748) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * make connector form work in connector builder * wip * fix small stuff * add basic input UI * user inputs * make most of inputs configuration work * fix a bunch of stuff * handle unknown config types * add warning label * fix label * fix some styling * review comments * improve state management and error handling * allow auth configuration * check for conflicts with the inferred inputs * fix invisible inputs * handle stored form values that don't contain new fields properly * session token and oauth authentication * fill in session token variable * fix merge of default values * add primaryKey and cursorField to builder types, and consolidate default valeues to types.ts * add cursor and primary key fields to ui * save * add page size and token option inputs * fixes after rebase * add pagination * fix pagination types * handle empty field_name better * Update airbyte-webapp/src/locales/en.json Co-authored-by: Lake Mossman * Update airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx Co-authored-by: Lake Mossman * inputs editing weirdness * input form reset * using the Label component * reduce redundancy and hide advanced input options for inferred inputs * unnecessary validation * typo * unnecessary effect hook * build spec even for invalid forms but do not update stream list * typos * make sure validation error does not go away * make primary key and cursor optional, and reorder * save toggle group progress * fix style of toggle label * handle empty values better * fix page size/token option field validation and rendering * handle cursor pagination page size option correctly * save stream slicer progress * finish stream slicer * fix stream slicer fields and validation Co-authored-by: Joe Reuter * debounce form builder values update to reduce load * 🪟🔧 Connector builder: use new lowcode manifest (#20715) * use new manifest yaml * Update airbyte-webapp/src/components/connectorBuilder/types.ts Co-authored-by: Lake Mossman * use updated manifest types Co-authored-by: Lake Mossman * debounce validation as well * akways show stream test button in error state if there are errors * fix type of oauth input * review comments * fix more * add validation schema for add stream form * validate all views on test click * add type to prevent console warning * review comment * make sure testing state and form state stay consistent * improve builder errors * remove test state from streamconfig view * remove console log * remove unnecessary positive index check Co-authored-by: Tim Roes Co-authored-by: lmossman --- .../connectorBuilder/Builder/Builder.tsx | 4 +- .../Builder/BuilderSidebar.tsx | 12 +- .../Builder/StreamConfigView.tsx | 6 +- .../connectorBuilder/DownloadYamlButton.tsx | 4 +- .../StreamTestingPanel/ConfigMenu.tsx | 21 +-- .../StreamTestingPanel/StreamSelector.tsx | 10 +- .../StreamTestingPanel/StreamTestButton.tsx | 12 +- .../StreamTestingPanel/StreamTester.tsx | 16 +- .../StreamTestingPanel/StreamTestingPanel.tsx | 20 ++- .../YamlEditor/YamlEditor.tsx | 4 +- .../connectorBuilder/useBuilderErrors.ts | 7 +- .../ConnectorBuilderPage.tsx | 136 ++++++++++------- .../ConnectorBuilderStateService.tsx | 138 +++++++++++------- 13 files changed, 232 insertions(+), 158 deletions(-) diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/Builder.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/Builder.tsx index 6596005a883c..b042352273ce 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/Builder.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/Builder.tsx @@ -2,7 +2,7 @@ import { Form } from "formik"; import debounce from "lodash/debounce"; import { useEffect, useMemo } from "react"; -import { BuilderView, useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; +import { BuilderView, useConnectorBuilderFormState } from "services/connectorBuilder/ConnectorBuilderStateService"; import { builderFormValidationSchema, BuilderFormValues } from "../types"; import styles from "./Builder.module.scss"; @@ -29,7 +29,7 @@ function getView(selectedView: BuilderView, hasMultipleStreams: boolean) { } export const Builder: React.FC = ({ values, toggleYamlEditor, validateForm }) => { - const { setBuilderFormValues, selectedView } = useConnectorBuilderState(); + const { setBuilderFormValues, selectedView } = useConnectorBuilderFormState(); const debouncedSetBuilderFormValues = useMemo( () => debounce((values) => { diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.tsx index 7bd54599ea6b..d8197ce8ff98 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.tsx @@ -2,6 +2,7 @@ import { faSliders, faUser } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import classnames from "classnames"; import { useFormikContext } from "formik"; +import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import Indicator from "components/Indicator"; @@ -10,7 +11,7 @@ import { Heading } from "components/ui/Heading"; import { Text } from "components/ui/Text"; import { useConfirmationModalService } from "hooks/services/ConfirmationModal"; -import { BuilderView, useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; +import { BuilderView, useConnectorBuilderFormState } from "services/connectorBuilder/ConnectorBuilderStateService"; import { DownloadYamlButton } from "../DownloadYamlButton"; import { BuilderFormValues, DEFAULT_BUILDER_FORM_VALUES, getInferredInputs } from "../types"; @@ -52,11 +53,11 @@ interface BuilderSidebarProps { toggleYamlEditor: () => void; } -export const BuilderSidebar: React.FC = ({ className, toggleYamlEditor }) => { +export const BuilderSidebar: React.FC = React.memo(({ className, toggleYamlEditor }) => { const { formatMessage } = useIntl(); const { hasErrors } = useBuilderErrors(); const { openConfirmationModal, closeConfirmationModal } = useConfirmationModalService(); - const { yamlManifest, selectedView, setSelectedView, setTestStreamIndex } = useConnectorBuilderState(); + const { yamlManifest, selectedView, setSelectedView } = useConnectorBuilderFormState(); const { values, setValues } = useFormikContext(); const handleResetForm = () => { openConfirmationModal({ @@ -72,9 +73,6 @@ export const BuilderSidebar: React.FC = ({ className, toggl }; const handleViewSelect = (selectedView: BuilderView) => { setSelectedView(selectedView); - if (selectedView !== "global" && selectedView !== "inputs") { - setTestStreamIndex(selectedView); - } }; return ( @@ -150,4 +148,4 @@ export const BuilderSidebar: React.FC = ({ className, toggl ); -}; +}); diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/StreamConfigView.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/StreamConfigView.tsx index 96d344b2a4f2..ee15ef0cae42 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/StreamConfigView.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/StreamConfigView.tsx @@ -10,7 +10,7 @@ import { CodeEditor } from "components/ui/CodeEditor"; import { Text } from "components/ui/Text"; import { useConfirmationModalService } from "hooks/services/ConfirmationModal"; -import { BuilderView, useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; +import { BuilderView, useConnectorBuilderFormState } from "services/connectorBuilder/ConnectorBuilderStateService"; import { BuilderStream } from "../types"; import { AddStreamButton } from "./AddStreamButton"; @@ -33,7 +33,7 @@ export const StreamConfigView: React.FC = ({ streamNum, h const [field, , helpers] = useField("streams"); const [selectedTab, setSelectedTab] = useState<"configuration" | "schema">("configuration"); const { openConfirmationModal, closeConfirmationModal } = useConfirmationModalService(); - const { setSelectedView, setTestStreamIndex } = useConnectorBuilderState(); + const { setSelectedView } = useConnectorBuilderFormState(); const streamPath = `streams[${streamNum}]`; const streamFieldPath = (fieldPath: string) => `${streamPath}.${fieldPath}`; @@ -49,7 +49,6 @@ export const StreamConfigView: React.FC = ({ streamNum, h const viewToSelect: BuilderView = updatedStreams.length === 0 ? "global" : streamToSelect; helpers.setValue(updatedStreams); setSelectedView(viewToSelect); - setTestStreamIndex(streamToSelect); closeConfirmationModal(); }, }); @@ -80,7 +79,6 @@ export const StreamConfigView: React.FC = ({ streamNum, h { setSelectedView(addedStreamNum); - setTestStreamIndex(addedStreamNum); }} initialValues={field.value[streamNum]} button={ diff --git a/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.tsx b/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.tsx index 55a6d1f0170a..ffe02b1fd34c 100644 --- a/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.tsx @@ -5,7 +5,7 @@ import { FormattedMessage } from "react-intl"; import { Button } from "components/ui/Button"; import { Tooltip } from "components/ui/Tooltip"; -import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; +import { useConnectorBuilderFormState } from "services/connectorBuilder/ConnectorBuilderStateService"; import { downloadFile } from "utils/file"; import styles from "./DownloadYamlButton.module.scss"; @@ -18,7 +18,7 @@ interface DownloadYamlButtonProps { } export const DownloadYamlButton: React.FC = ({ className, yaml, yamlIsValid }) => { - const { editorView } = useConnectorBuilderState(); + const { editorView } = useConnectorBuilderFormState(); const { hasErrors, validateAndTouch } = useBuilderErrors(); const downloadYaml = () => { diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/ConfigMenu.tsx b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/ConfigMenu.tsx index d9e98fbe4e72..1455e91c3166 100644 --- a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/ConfigMenu.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/ConfigMenu.tsx @@ -12,7 +12,8 @@ import { Tooltip } from "components/ui/Tooltip"; import { SourceDefinitionSpecificationDraft } from "core/domain/connector"; import { StreamReadRequestBodyConfig } from "core/request/ConnectorBuilderClient"; -import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; +import { useConnectorBuilderTestState } from "services/connectorBuilder/ConnectorBuilderStateService"; +import { useConnectorBuilderFormState } from "services/connectorBuilder/ConnectorBuilderStateService"; import { ConnectorForm } from "views/Connector/ConnectorForm"; import styles from "./ConfigMenu.module.scss"; @@ -20,14 +21,16 @@ import { ConfigMenuErrorBoundaryComponent } from "./ConfigMenuErrorBoundary"; interface ConfigMenuProps { className?: string; - configJsonErrors: number; + testInputJsonErrors: number; isOpen: boolean; setIsOpen: (open: boolean) => void; } -export const ConfigMenu: React.FC = ({ className, configJsonErrors, isOpen, setIsOpen }) => { +export const ConfigMenu: React.FC = ({ className, testInputJsonErrors, isOpen, setIsOpen }) => { const { formatMessage } = useIntl(); - const { configJson, setConfigJson, jsonManifest, editorView, setEditorView } = useConnectorBuilderState(); + const { jsonManifest, editorView, setEditorView } = useConnectorBuilderFormState(); + + const { testInputJson, setTestInputJson } = useConnectorBuilderTestState(); const [showInputsWarning, setShowInputsWarning] = useLocalStorage("connectorBuilderInputsWarning", true); @@ -64,8 +67,8 @@ export const ConfigMenu: React.FC = ({ className, configJsonErr > - {configJsonErrors > 0 && ( - + {testInputJsonErrors > 0 && ( + )} } @@ -110,16 +113,16 @@ export const ConfigMenu: React.FC = ({ className, configJsonErr bodyClassName={styles.formContent} footerClassName={styles.inputFormModalFooter} selectedConnectorDefinitionSpecification={connectorDefinitionSpecification} - formValues={{ connectionConfiguration: configJson }} + formValues={{ connectionConfiguration: testInputJson }} onSubmit={async (values) => { - setConfigJson(values.connectionConfiguration as StreamReadRequestBodyConfig); + setTestInputJson(values.connectionConfiguration as StreamReadRequestBodyConfig); setIsOpen(false); }} onCancel={() => { setIsOpen(false); }} onReset={() => { - setConfigJson({}); + setTestInputJson({}); }} submitLabel={formatMessage({ id: "connectorBuilder.saveInputsForm" })} /> diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamSelector.tsx b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamSelector.tsx index 20d3e8189a48..198397229593 100644 --- a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamSelector.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamSelector.tsx @@ -5,7 +5,10 @@ import { useIntl } from "react-intl"; import { Heading } from "components/ui/Heading"; import { ListBox, ListBoxControlButtonProps } from "components/ui/ListBox"; -import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; +import { + useConnectorBuilderTestState, + useConnectorBuilderFormState, +} from "services/connectorBuilder/ConnectorBuilderStateService"; import { ReactComponent as CaretDownIcon } from "../../ui/ListBox/CaretDownIcon.svg"; import styles from "./StreamSelector.module.scss"; @@ -27,7 +30,8 @@ const ControlButton: React.FC> = ({ selectedOp export const StreamSelector: React.FC = ({ className }) => { const { formatMessage } = useIntl(); - const { streams, selectedView, testStreamIndex, setSelectedView, setTestStreamIndex } = useConnectorBuilderState(); + const { selectedView, setSelectedView } = useConnectorBuilderFormState(); + const { streams, testStreamIndex, setTestStreamIndex } = useConnectorBuilderTestState(); const options = streams.map((stream) => { const label = stream.name && stream.name.trim() ? capitalize(stream.name) : formatMessage({ id: "connectorBuilder.emptyName" }); @@ -39,7 +43,7 @@ export const StreamSelector: React.FC = ({ className }) => if (selectedStreamIndex >= 0) { setTestStreamIndex(selectedStreamIndex); - if (selectedView !== "global" && selectedStreamIndex >= 0) { + if (selectedView !== "global") { setSelectedView(selectedStreamIndex); } } diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestButton.tsx b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestButton.tsx index 30fe955bbbff..a1a2caf051da 100644 --- a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestButton.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestButton.tsx @@ -7,27 +7,27 @@ import { Button } from "components/ui/Button"; import { Text } from "components/ui/Text"; import { Tooltip } from "components/ui/Tooltip"; -import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; +import { useConnectorBuilderFormState } from "services/connectorBuilder/ConnectorBuilderStateService"; import { useBuilderErrors } from "../useBuilderErrors"; import styles from "./StreamTestButton.module.scss"; interface StreamTestButtonProps { readStream: () => void; - hasConfigJsonErrors: boolean; + hasTestInputJsonErrors: boolean; setTestInputOpen: (open: boolean) => void; } export const StreamTestButton: React.FC = ({ readStream, - hasConfigJsonErrors, + hasTestInputJsonErrors, setTestInputOpen, }) => { - const { editorView, yamlIsValid } = useConnectorBuilderState(); + const { editorView, yamlIsValid } = useConnectorBuilderFormState(); const { hasErrors, validateAndTouch } = useBuilderErrors(); const handleClick = () => { - if (hasConfigJsonErrors) { + if (hasTestInputJsonErrors) { setTestInputOpen(true); return; } @@ -49,7 +49,7 @@ export const StreamTestButton: React.FC = ({ tooltipContent = ; } - if ((editorView === "ui" && hasErrors(false)) || hasConfigJsonErrors) { + if ((editorView === "ui" && hasErrors(false)) || hasTestInputJsonErrors) { showWarningIcon = true; tooltipContent = ; } diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.tsx b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.tsx index dc8027a6d0a9..d91026295938 100644 --- a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.tsx @@ -6,7 +6,10 @@ import { Spinner } from "components/ui/Spinner"; import { Text } from "components/ui/Text"; import { useReadStream } from "services/connectorBuilder/ConnectorBuilderApiService"; -import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; +import { + useConnectorBuilderTestState, + useConnectorBuilderFormState, +} from "services/connectorBuilder/ConnectorBuilderStateService"; import { LogsDisplay } from "./LogsDisplay"; import { ResultDisplay } from "./ResultDisplay"; @@ -14,11 +17,12 @@ import { StreamTestButton } from "./StreamTestButton"; import styles from "./StreamTester.module.scss"; export const StreamTester: React.FC<{ - hasConfigJsonErrors: boolean; + hasTestInputJsonErrors: boolean; setTestInputOpen: (open: boolean) => void; -}> = ({ hasConfigJsonErrors, setTestInputOpen }) => { +}> = ({ hasTestInputJsonErrors, setTestInputOpen }) => { const { formatMessage } = useIntl(); - const { jsonManifest, configJson, streams, testStreamIndex } = useConnectorBuilderState(); + const { jsonManifest } = useConnectorBuilderFormState(); + const { streams, testInputJson, testStreamIndex } = useConnectorBuilderTestState(); const { data: streamReadData, refetch: readStream, @@ -28,7 +32,7 @@ export const StreamTester: React.FC<{ } = useReadStream({ manifest: jsonManifest, stream: streams[testStreamIndex]?.name, - config: configJson, + config: testInputJson, }); const [logsFlex, setLogsFlex] = useState(0); @@ -60,7 +64,7 @@ export const StreamTester: React.FC<{ diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestingPanel.tsx b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestingPanel.tsx index b3bff5d70d38..69bfc96573fc 100644 --- a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestingPanel.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestingPanel.tsx @@ -10,7 +10,10 @@ import { jsonSchemaToFormBlock } from "core/form/schemaToFormBlock"; import { buildYupFormForJsonSchema } from "core/form/schemaToYup"; import { StreamReadRequestBodyConfig } from "core/request/ConnectorBuilderClient"; import { Spec } from "core/request/ConnectorManifest"; -import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; +import { + useConnectorBuilderTestState, + useConnectorBuilderFormState, +} from "services/connectorBuilder/ConnectorBuilderStateService"; import { links } from "utils/links"; import { ConfigMenu } from "./ConfigMenu"; @@ -20,13 +23,13 @@ import styles from "./StreamTestingPanel.module.scss"; const EMPTY_SCHEMA = {}; -function useConfigJsonErrors(configJson: StreamReadRequestBodyConfig, spec?: Spec): number { +function useTestInputJsonErrors(testInputJson: StreamReadRequestBodyConfig, spec?: Spec): number { return useMemo(() => { try { const jsonSchema = spec && spec.connection_specification ? spec.connection_specification : EMPTY_SCHEMA; const formFields = jsonSchemaToFormBlock(jsonSchema); const validationSchema = buildYupFormForJsonSchema(jsonSchema, formFields); - validationSchema.validateSync(configJson, { abortEarly: false }); + validationSchema.validateSync(testInputJson, { abortEarly: false }); return 0; } catch (e) { if (ValidationError.isError(e)) { @@ -34,14 +37,15 @@ function useConfigJsonErrors(configJson: StreamReadRequestBodyConfig, spec?: Spe } return 1; } - }, [configJson, spec]); + }, [testInputJson, spec]); } export const StreamTestingPanel: React.FC = () => { const [isTestInputOpen, setTestInputOpen] = useState(false); - const { jsonManifest, configJson, streamListErrorMessage, yamlEditorIsMounted } = useConnectorBuilderState(); + const { jsonManifest, yamlEditorIsMounted } = useConnectorBuilderFormState(); + const { testInputJson, streamListErrorMessage } = useConnectorBuilderTestState(); - const configJsonErrors = useConfigJsonErrors(configJson, jsonManifest.spec); + const testInputJsonErrors = useTestInputJsonErrors(testInputJson, jsonManifest.spec); if (!yamlEditorIsMounted) { return ( @@ -57,7 +61,7 @@ export const StreamTestingPanel: React.FC = () => {
@@ -72,7 +76,7 @@ export const StreamTestingPanel: React.FC = () => { {hasStreams && streamListErrorMessage === undefined && (
- 0} setTestInputOpen={setTestInputOpen} /> + 0} setTestInputOpen={setTestInputOpen} />
)} {hasStreams && streamListErrorMessage !== undefined && ( diff --git a/airbyte-webapp/src/components/connectorBuilder/YamlEditor/YamlEditor.tsx b/airbyte-webapp/src/components/connectorBuilder/YamlEditor/YamlEditor.tsx index 39353461c95f..f62b0c1a2514 100644 --- a/airbyte-webapp/src/components/connectorBuilder/YamlEditor/YamlEditor.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/YamlEditor/YamlEditor.tsx @@ -9,7 +9,7 @@ import { CodeEditor } from "components/ui/CodeEditor"; import { ConnectorManifest } from "core/request/ConnectorManifest"; import { useConfirmationModalService } from "hooks/services/ConfirmationModal"; -import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; +import { useConnectorBuilderFormState } from "services/connectorBuilder/ConnectorBuilderStateService"; import { UiYamlToggleButton } from "../Builder/UiYamlToggleButton"; import { DownloadYamlButton } from "../DownloadYamlButton"; @@ -31,7 +31,7 @@ export const YamlEditor: React.FC = ({ toggleYamlEditor }) => { setYamlEditorIsMounted, setYamlIsValid, setJsonManifest, - } = useConnectorBuilderState(); + } = useConnectorBuilderFormState(); const [yamlValue, setYamlValue] = useState(yamlManifest); // debounce the setJsonManifest calls so that it doesnt result in a network call for every keystroke diff --git a/airbyte-webapp/src/components/connectorBuilder/useBuilderErrors.ts b/airbyte-webapp/src/components/connectorBuilder/useBuilderErrors.ts index 88bbf1411e12..5ee008f51c81 100644 --- a/airbyte-webapp/src/components/connectorBuilder/useBuilderErrors.ts +++ b/airbyte-webapp/src/components/connectorBuilder/useBuilderErrors.ts @@ -3,13 +3,13 @@ import { FormikErrors, useFormikContext } from "formik"; import intersection from "lodash/intersection"; import { useCallback } from "react"; -import { BuilderView, useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; +import { BuilderView, useConnectorBuilderFormState } from "services/connectorBuilder/ConnectorBuilderStateService"; import { BuilderFormValues } from "./types"; export const useBuilderErrors = () => { const { touched, errors, validateForm, setFieldTouched } = useFormikContext(); - const { setSelectedView, setTestStreamIndex } = useConnectorBuilderState(); + const { setSelectedView } = useConnectorBuilderFormState(); const invalidViews = useCallback( (ignoreUntouched: boolean, limitToViews?: BuilderView[], inputErrors?: FormikErrors) => { @@ -87,14 +87,13 @@ export const useBuilderErrors = () => { setSelectedView("global"); } else { setSelectedView(invalidBuilderViews[0]); - setTestStreamIndex(invalidBuilderViews[0] as number); } } else { callback(); } }); }, - [invalidViews, setFieldTouched, setSelectedView, setTestStreamIndex, validateForm] + [invalidViews, setFieldTouched, setSelectedView, validateForm] ); return { hasErrors, validateAndTouch }; diff --git a/airbyte-webapp/src/pages/ConnectorBuilderPage/ConnectorBuilderPage.tsx b/airbyte-webapp/src/pages/ConnectorBuilderPage/ConnectorBuilderPage.tsx index 765efd547b00..be7c38afaced 100644 --- a/airbyte-webapp/src/pages/ConnectorBuilderPage/ConnectorBuilderPage.tsx +++ b/airbyte-webapp/src/pages/ConnectorBuilderPage/ConnectorBuilderPage.tsx @@ -1,77 +1,103 @@ import classnames from "classnames"; import { Formik } from "formik"; +import React, { useCallback, useMemo, useRef } from "react"; import { useIntl } from "react-intl"; import { Builder } from "components/connectorBuilder/Builder/Builder"; import { StreamTestingPanel } from "components/connectorBuilder/StreamTestingPanel"; -import { builderFormValidationSchema } from "components/connectorBuilder/types"; +import { builderFormValidationSchema, BuilderFormValues } from "components/connectorBuilder/types"; import { YamlEditor } from "components/connectorBuilder/YamlEditor"; import { ResizablePanels } from "components/ui/ResizablePanels"; import { - ConnectorBuilderStateProvider, - useConnectorBuilderState, + ConnectorBuilderTestStateProvider, + ConnectorBuilderFormStateProvider, + useConnectorBuilderFormState, } from "services/connectorBuilder/ConnectorBuilderStateService"; import styles from "./ConnectorBuilderPage.module.scss"; -const ConnectorBuilderPageInner: React.FC = () => { - const { formatMessage } = useIntl(); - const { builderFormValues, editorView, setEditorView } = useConnectorBuilderState(); +// eslint-disable-next-line @typescript-eslint/no-empty-function +const noop = function () {}; - return ( - undefined} - validationSchema={builderFormValidationSchema} - validateOnChange={false} - > - {({ values, validateForm }) => { - return ( - - {editorView === "yaml" ? ( - setEditorView("ui")} /> - ) : ( - setEditorView("yaml")} - validateForm={validateForm} - /> - )} - - ), - className: styles.leftPanel, - minWidth: 100, - }} - secondPanel={{ - children: , - className: styles.rightPanel, - flex: 0.33, - minWidth: 60, - overlay: { - displayThreshold: 325, - header: formatMessage({ id: "connectorBuilder.testConnector" }), - rotation: "counter-clockwise", - }, - }} +const ConnectorBuilderPageInner: React.FC = React.memo(() => { + const { builderFormValues, editorView, setEditorView } = useConnectorBuilderFormState(); + + const switchToUI = useCallback(() => setEditorView("ui"), [setEditorView]); + const switchToYaml = useCallback(() => setEditorView("yaml"), [setEditorView]); + + const initialFormValues = useRef(builderFormValues); + return useMemo( + () => ( + + {({ values, validateForm }) => ( + - ); - }} - + )} + + ), + [editorView, switchToUI, switchToYaml] ); -}; +}); export const ConnectorBuilderPage: React.FC = () => ( - - - + + + + + +); + +const Panels = React.memo( + ({ + editorView, + switchToUI, + switchToYaml, + values, + validateForm, + }: { + editorView: string; + switchToUI: () => void; + values: BuilderFormValues; + switchToYaml: () => void; + validateForm: () => void; + }) => { + const { formatMessage } = useIntl(); + return ( + + {editorView === "yaml" ? ( + + ) : ( + + )} + + ), + className: styles.leftPanel, + minWidth: 100, + }} + secondPanel={{ + children: , + className: styles.rightPanel, + flex: 0.33, + minWidth: 60, + overlay: { + displayThreshold: 325, + header: formatMessage({ id: "connectorBuilder.testConnector" }), + rotation: "counter-clockwise", + }, + }} + /> + ); + } ); export default ConnectorBuilderPage; diff --git a/airbyte-webapp/src/services/connectorBuilder/ConnectorBuilderStateService.tsx b/airbyte-webapp/src/services/connectorBuilder/ConnectorBuilderStateService.tsx index 356afdf64dee..6e35db96000b 100644 --- a/airbyte-webapp/src/services/connectorBuilder/ConnectorBuilderStateService.tsx +++ b/airbyte-webapp/src/services/connectorBuilder/ConnectorBuilderStateService.tsx @@ -7,7 +7,7 @@ import { useLocalStorage } from "react-use"; import { BuilderFormValues, convertToManifest, DEFAULT_BUILDER_FORM_VALUES } from "components/connectorBuilder/types"; import { StreamReadRequestBodyConfig, StreamsListReadStreamsItem } from "core/request/ConnectorBuilderClient"; -import { ConnectorManifest } from "core/request/ConnectorManifest"; +import { ConnectorManifest, DeclarativeComponentSchema } from "core/request/ConnectorManifest"; import { useListStreams } from "./ConnectorBuilderApiService"; @@ -24,33 +24,36 @@ const DEFAULT_JSON_MANIFEST_VALUES: ConnectorManifest = { export type EditorView = "ui" | "yaml"; export type BuilderView = "global" | "inputs" | number; -interface Context { +interface FormStateContext { builderFormValues: BuilderFormValues; jsonManifest: ConnectorManifest; + lastValidJsonManifest: DeclarativeComponentSchema | undefined; yamlManifest: string; yamlEditorIsMounted: boolean; yamlIsValid: boolean; - streams: StreamsListReadStreamsItem[]; - streamListErrorMessage: string | undefined; - testStreamIndex: number; selectedView: BuilderView; - configJson: StreamReadRequestBodyConfig; editorView: EditorView; setBuilderFormValues: (values: BuilderFormValues, isInvalid: boolean) => void; setJsonManifest: (jsonValue: ConnectorManifest) => void; setYamlEditorIsMounted: (value: boolean) => void; setYamlIsValid: (value: boolean) => void; - setTestStreamIndex: (streamIndex: number) => void; setSelectedView: (view: BuilderView) => void; - setConfigJson: (value: StreamReadRequestBodyConfig) => void; setEditorView: (editorView: EditorView) => void; } -export const ConnectorBuilderStateContext = React.createContext(null); +interface TestStateContext { + streams: StreamsListReadStreamsItem[]; + streamListErrorMessage: string | undefined; + testInputJson: StreamReadRequestBodyConfig; + setTestInputJson: (value: StreamReadRequestBodyConfig) => void; + setTestStreamIndex: (streamIndex: number) => void; + testStreamIndex: number; +} -export const ConnectorBuilderStateProvider: React.FC> = ({ children }) => { - const { formatMessage } = useIntl(); +export const ConnectorBuilderFormStateContext = React.createContext(null); +export const ConnectorBuilderTestStateContext = React.createContext(null); +export const ConnectorBuilderFormStateProvider: React.FC> = ({ children }) => { // manifest values const [storedBuilderFormValues, setStoredBuilderFormValues] = useLocalStorage( "connectorBuilderFormValues", @@ -61,10 +64,11 @@ export const ConnectorBuilderStateProvider: React.FC { - setStoredBuilderFormValues(values); if (isValid) { + // update ref first because calling setStoredBuilderFormValues might synchronously kick off a react render cycle. lastValidBuilderFormValuesRef.current = values; } + setStoredBuilderFormValues(values); }, [setStoredBuilderFormValues] ); @@ -79,19 +83,31 @@ export const ConnectorBuilderStateProvider: React.FC { - setJsonManifest(convertToManifest(builderFormValues)); - }, [builderFormValues, setJsonManifest]); + const [editorView, rawSetEditorView] = useLocalStorage("connectorBuilderEditorView", "ui"); + + const derivedJsonManifest = useMemo( + () => (editorView === "yaml" ? manifest : convertToManifest(builderFormValues)), + [editorView, builderFormValues, manifest] + ); + + const manifestRef = useRef(derivedJsonManifest); + manifestRef.current = derivedJsonManifest; + + const setEditorView = useCallback( + (view: EditorView) => { + if (view === "yaml") { + // when switching to yaml, store the currently derived json manifest + setJsonManifest(manifestRef.current); + } + rawSetEditorView(view); + }, + [rawSetEditorView, setJsonManifest] + ); const [yamlIsValid, setYamlIsValid] = useState(true); const [yamlEditorIsMounted, setYamlEditorIsMounted] = useState(true); - const [yamlManifest, setYamlManifest] = useState(""); - useEffect(() => { - setYamlManifest(dump(jsonManifest)); - }, [jsonManifest]); - - const [editorView, setEditorView] = useState("ui"); + const yamlManifest = useMemo(() => dump(derivedJsonManifest), [derivedJsonManifest]); const lastValidBuilderFormValues = lastValidBuilderFormValuesRef.current; /** @@ -101,22 +117,50 @@ export const ConnectorBuilderStateProvider: React.FC editorView !== "ui" - ? undefined + ? jsonManifest : builderFormValues === lastValidBuilderFormValues ? jsonManifest : convertToManifest(lastValidBuilderFormValues), [builderFormValues, editorView, jsonManifest, lastValidBuilderFormValues] ); + const [selectedView, setSelectedView] = useState("global"); + + const ctx = { + builderFormValues, + jsonManifest: derivedJsonManifest, + lastValidJsonManifest, + yamlManifest, + yamlEditorIsMounted, + yamlIsValid, + selectedView, + editorView: editorView || "ui", + setBuilderFormValues, + setJsonManifest, + setYamlIsValid, + setYamlEditorIsMounted, + setSelectedView, + setEditorView, + }; + + return {children}; +}; + +export const ConnectorBuilderTestStateProvider: React.FC> = ({ children }) => { + const { formatMessage } = useIntl(); + const { lastValidJsonManifest, selectedView } = useConnectorBuilderFormState(); + + const manifest = lastValidJsonManifest ?? DEFAULT_JSON_MANIFEST_VALUES; + // config - const [configJson, setConfigJson] = useState({}); + const [testInputJson, setTestInputJson] = useState({}); // streams const { data: streamListRead, isError: isStreamListError, error: streamListError, - } = useListStreams({ manifest: lastValidJsonManifest || manifest, config: configJson }); + } = useListStreams({ manifest, config: testInputJson }); const unknownErrorMessage = formatMessage({ id: "connectorBuilder.unknownError" }); const streamListErrorMessage = isStreamListError ? streamListError instanceof Error @@ -129,49 +173,43 @@ export const ConnectorBuilderStateProvider: React.FC { - setTestStreamIndex((prevIndex) => - prevIndex >= streams.length && streams.length > 0 ? streams.length - 1 : prevIndex - ); - }, [streams]); - - const [selectedView, setSelectedView] = useState("global"); + if (typeof selectedView === "number") { + setTestStreamIndex(selectedView); + } + }, [selectedView]); const ctx = { - builderFormValues, - jsonManifest: manifest, - yamlManifest, - yamlEditorIsMounted, - yamlIsValid, streams, streamListErrorMessage, + testInputJson, + setTestInputJson, testStreamIndex, - selectedView, - configJson, - editorView, - setBuilderFormValues, - setJsonManifest, - setYamlIsValid, - setYamlEditorIsMounted, setTestStreamIndex, - setSelectedView, - setConfigJson, - setEditorView, }; - return {children}; + return {children}; +}; + +export const useConnectorBuilderTestState = (): TestStateContext => { + const connectorBuilderState = useContext(ConnectorBuilderTestStateContext); + if (!connectorBuilderState) { + throw new Error("useConnectorBuilderTestStae must be used within a ConnectorBuilderTestStateProvider."); + } + + return connectorBuilderState; }; -export const useConnectorBuilderState = (): Context => { - const connectorBuilderState = useContext(ConnectorBuilderStateContext); +export const useConnectorBuilderFormState = (): FormStateContext => { + const connectorBuilderState = useContext(ConnectorBuilderFormStateContext); if (!connectorBuilderState) { - throw new Error("useConnectorBuilderState must be used within a ConnectorBuilderStateProvider."); + throw new Error("useConnectorBuilderFormState must be used within a ConnectorBuilderFormStateProvider."); } return connectorBuilderState; }; export const useSelectedPageAndSlice = () => { - const { streams, testStreamIndex } = useConnectorBuilderState(); + const { streams, testStreamIndex } = useConnectorBuilderTestState(); const selectedStreamName = streams[testStreamIndex].name; From 7beef18802ca2a26986af333ba42affdb6735d73 Mon Sep 17 00:00:00 2001 From: Anne <102554163+alovew@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:30:26 -0800 Subject: [PATCH 007/170] Remove workspace helper from fetchConfigActivity (#21048) * Remove workspace helper and replace with workspaceApi --- .../workers/config/ApiClientBeanFactory.java | 6 +++ .../ConnectionNotificationWorkflowImpl.java | 27 ++++++---- .../activities/ConfigFetchActivity.java | 9 ++-- .../activities/ConfigFetchActivityImpl.java | 54 +++++++++---------- .../ConnectionNotificationWorkflowTest.java | 3 +- .../activities/ConfigFetchActivityTest.java | 33 ++++++------ 6 files changed, 67 insertions(+), 65 deletions(-) diff --git a/airbyte-workers/src/main/java/io/airbyte/workers/config/ApiClientBeanFactory.java b/airbyte-workers/src/main/java/io/airbyte/workers/config/ApiClientBeanFactory.java index 0b4476d544cd..60eb6604e7bf 100644 --- a/airbyte-workers/src/main/java/io/airbyte/workers/config/ApiClientBeanFactory.java +++ b/airbyte-workers/src/main/java/io/airbyte/workers/config/ApiClientBeanFactory.java @@ -11,6 +11,7 @@ import io.airbyte.api.client.AirbyteApiClient; import io.airbyte.api.client.generated.ConnectionApi; import io.airbyte.api.client.generated.SourceApi; +import io.airbyte.api.client.generated.WorkspaceApi; import io.airbyte.api.client.invoker.generated.ApiClient; import io.airbyte.commons.temporal.config.WorkerMode; import io.micronaut.context.BeanProvider; @@ -73,6 +74,11 @@ public ConnectionApi connectionApi(final ApiClient apiClient) { return new ConnectionApi(apiClient); } + @Singleton + public WorkspaceApi workspaceApi(final ApiClient apiClient) { + return new WorkspaceApi(apiClient); + } + @Singleton public HttpClient httpClient() { return HttpClient.newHttpClient(); diff --git a/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/ConnectionNotificationWorkflowImpl.java b/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/ConnectionNotificationWorkflowImpl.java index 3a233a340168..fb16b41346e2 100644 --- a/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/ConnectionNotificationWorkflowImpl.java +++ b/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/ConnectionNotificationWorkflowImpl.java @@ -9,7 +9,6 @@ import io.airbyte.config.Notification; import io.airbyte.config.Notification.NotificationType; import io.airbyte.config.SlackNotificationConfiguration; -import io.airbyte.config.StandardSync; import io.airbyte.config.persistence.ConfigNotFoundException; import io.airbyte.notification.SlackNotificationClient; import io.airbyte.validation.json.JsonValidationException; @@ -17,6 +16,7 @@ import io.airbyte.workers.temporal.scheduling.activities.ConfigFetchActivity; import io.airbyte.workers.temporal.scheduling.activities.NotifySchemaChangeActivity; import io.airbyte.workers.temporal.scheduling.activities.SlackConfigActivity; +import io.temporal.workflow.Workflow; import java.io.IOException; import java.util.Optional; import java.util.UUID; @@ -25,6 +25,9 @@ @Slf4j public class ConnectionNotificationWorkflowImpl implements ConnectionNotificationWorkflow { + private static final String GET_BREAKING_CHANGE_TAG = "get_breaking_change"; + private static final int GET_BREAKING_CHANGE_VERSION = 1; + @TemporalActivityStub(activityOptionsBeanName = "shortActivityOptions") private NotifySchemaChangeActivity notifySchemaChangeActivity; @TemporalActivityStub(activityOptionsBeanName = "shortActivityOptions") @@ -35,14 +38,20 @@ public class ConnectionNotificationWorkflowImpl implements ConnectionNotificatio @Override public boolean sendSchemaChangeNotification(final UUID connectionId) throws IOException, InterruptedException, ApiException, ConfigNotFoundException, JsonValidationException { - final StandardSync standardSync = configFetchActivity.getStandardSync(connectionId); - final Optional slackConfig = slackConfigActivity.fetchSlackConfiguration(connectionId); - if (slackConfig.isPresent()) { - final Notification notification = - new Notification().withNotificationType(NotificationType.SLACK).withSendOnFailure(false).withSendOnSuccess(false) - .withSlackConfiguration(slackConfig.get()); - final SlackNotificationClient notificationClient = new SlackNotificationClient(notification); - return notifySchemaChangeActivity.notifySchemaChange(notificationClient, connectionId, standardSync.getBreakingChange()); + final int getBreakingChangeVersion = + Workflow.getVersion(GET_BREAKING_CHANGE_TAG, Workflow.DEFAULT_VERSION, GET_BREAKING_CHANGE_VERSION); + if (getBreakingChangeVersion >= GET_BREAKING_CHANGE_VERSION) { + final Optional breakingChange = configFetchActivity.getBreakingChange(connectionId); + final Optional slackConfig = slackConfigActivity.fetchSlackConfiguration(connectionId); + if (slackConfig.isPresent() && breakingChange.isPresent()) { + final Notification notification = + new Notification().withNotificationType(NotificationType.SLACK).withSendOnFailure(false).withSendOnSuccess(false) + .withSlackConfiguration(slackConfig.get()); + final SlackNotificationClient notificationClient = new SlackNotificationClient(notification); + return notifySchemaChangeActivity.notifySchemaChange(notificationClient, connectionId, breakingChange.get()); + } else { + return false; + } } else { return false; } diff --git a/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/activities/ConfigFetchActivity.java b/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/activities/ConfigFetchActivity.java index 214cb323cd2e..6ad9c9c0f5f2 100644 --- a/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/activities/ConfigFetchActivity.java +++ b/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/activities/ConfigFetchActivity.java @@ -5,12 +5,8 @@ package io.airbyte.workers.temporal.scheduling.activities; import io.airbyte.api.client.model.generated.ConnectionStatus; -import io.airbyte.config.StandardSync; -import io.airbyte.config.persistence.ConfigNotFoundException; -import io.airbyte.validation.json.JsonValidationException; import io.temporal.activity.ActivityInterface; import io.temporal.activity.ActivityMethod; -import java.io.IOException; import java.time.Duration; import java.util.Optional; import java.util.UUID; @@ -27,6 +23,9 @@ public interface ConfigFetchActivity { @ActivityMethod Optional getStatus(UUID connectionId); + @ActivityMethod + public Optional getBreakingChange(final UUID connectionId); + @Data @NoArgsConstructor @AllArgsConstructor @@ -45,8 +44,6 @@ class ScheduleRetrieverOutput { } - StandardSync getStandardSync(final UUID connectionId) throws JsonValidationException, ConfigNotFoundException, IOException; - /** * Return how much time to wait before running the next sync. It will query the DB to get the last * starting time of the latest terminal job (Failed, canceled or successful) and return the amount diff --git a/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/activities/ConfigFetchActivityImpl.java b/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/activities/ConfigFetchActivityImpl.java index 7aff6ee2c3cd..36b046fa4f82 100644 --- a/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/activities/ConfigFetchActivityImpl.java +++ b/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/activities/ConfigFetchActivityImpl.java @@ -10,6 +10,7 @@ import com.google.common.annotations.VisibleForTesting; import datadog.trace.api.Trace; import io.airbyte.api.client.generated.ConnectionApi; +import io.airbyte.api.client.generated.WorkspaceApi; import io.airbyte.api.client.invoker.generated.ApiException; import io.airbyte.api.client.model.generated.ConnectionIdRequestBody; import io.airbyte.api.client.model.generated.ConnectionRead; @@ -19,16 +20,12 @@ import io.airbyte.api.client.model.generated.ConnectionScheduleDataCron; import io.airbyte.api.client.model.generated.ConnectionScheduleType; import io.airbyte.api.client.model.generated.ConnectionStatus; +import io.airbyte.api.client.model.generated.WorkspaceRead; import io.airbyte.commons.temporal.config.WorkerMode; import io.airbyte.commons.temporal.exception.RetryableException; -import io.airbyte.config.StandardSync; -import io.airbyte.config.persistence.ConfigNotFoundException; -import io.airbyte.config.persistence.ConfigRepository; import io.airbyte.metrics.lib.ApmTraceUtils; import io.airbyte.persistence.job.JobPersistence; -import io.airbyte.persistence.job.WorkspaceHelper; import io.airbyte.persistence.job.models.Job; -import io.airbyte.validation.json.JsonValidationException; import io.micronaut.context.annotation.Requires; import io.micronaut.context.annotation.Value; import jakarta.inject.Named; @@ -67,43 +64,25 @@ public class ConfigFetchActivityImpl implements ConfigFetchActivity { UUID.fromString("226edbc1-4a9c-4401-95a9-90435d667d9d")); private static final long SCHEDULING_NOISE_CONSTANT = 15; - private final ConfigRepository configRepository; private final JobPersistence jobPersistence; - private final WorkspaceHelper workspaceHelper; + private final WorkspaceApi workspaceApi; private final Integer syncJobMaxAttempts; private final Supplier currentSecondsSupplier; private final ConnectionApi connectionApi; - public ConfigFetchActivityImpl(final ConfigRepository configRepository, - final JobPersistence jobPersistence, - @Value("${airbyte.worker.sync.max-attempts}") final Integer syncJobMaxAttempts, - @Named("currentSecondsSupplier") final Supplier currentSecondsSupplier, - final ConnectionApi connectionApi) { - this(configRepository, jobPersistence, new WorkspaceHelper(configRepository, jobPersistence), syncJobMaxAttempts, currentSecondsSupplier, - connectionApi); - } - @VisibleForTesting - protected ConfigFetchActivityImpl(final ConfigRepository configRepository, - final JobPersistence jobPersistence, - final WorkspaceHelper workspaceHelper, + protected ConfigFetchActivityImpl(final JobPersistence jobPersistence, + final WorkspaceApi workspaceApi, @Value("${airbyte.worker.sync.max-attempts}") final Integer syncJobMaxAttempts, @Named("currentSecondsSupplier") final Supplier currentSecondsSupplier, final ConnectionApi connectionApi) { - this.configRepository = configRepository; this.jobPersistence = jobPersistence; - this.workspaceHelper = workspaceHelper; + this.workspaceApi = workspaceApi; this.syncJobMaxAttempts = syncJobMaxAttempts; this.currentSecondsSupplier = currentSecondsSupplier; this.connectionApi = connectionApi; } - @Trace(operationName = ACTIVITY_TRACE_OPERATION_NAME) - @Override - public StandardSync getStandardSync(final UUID connectionId) throws JsonValidationException, ConfigNotFoundException, IOException { - return configRepository.getStandardSync(connectionId); - } - @Trace(operationName = ACTIVITY_TRACE_OPERATION_NAME) @Override public ScheduleRetrieverOutput getTimeToWait(final ScheduleRetrieverInput input) { @@ -176,10 +155,12 @@ private ScheduleRetrieverOutput getTimeToWaitFromScheduleType(final ConnectionRe } private Duration addSchedulingNoiseForAllowListedWorkspace(Duration timeToWait, ConnectionRead connectionRead) { - final UUID workspaceId; + UUID workspaceId; try { - workspaceId = workspaceHelper.getWorkspaceForConnectionId(connectionRead.getConnectionId()); - } catch (JsonValidationException | ConfigNotFoundException e) { + ConnectionIdRequestBody connectionIdRequestBody = new ConnectionIdRequestBody().connectionId(connectionRead.getConnectionId()); + final WorkspaceRead workspaceRead = workspaceApi.getWorkspaceByConnectionId(connectionIdRequestBody); + workspaceId = workspaceRead.getWorkspaceId(); + } catch (ApiException e) { // We tolerate exceptions and fail open by doing nothing. return timeToWait; } @@ -264,6 +245,19 @@ public Optional getStatus(final UUID connectionId) { } } + @Override + public Optional getBreakingChange(final UUID connectionId) { + try { + final io.airbyte.api.client.model.generated.ConnectionIdRequestBody requestBody = + new io.airbyte.api.client.model.generated.ConnectionIdRequestBody().connectionId(connectionId); + final ConnectionRead connectionRead = connectionApi.getConnection(requestBody); + return Optional.ofNullable(connectionRead.getBreakingChange()); + } catch (ApiException e) { + log.info("Encountered an error fetching the connection's breaking change status: ", e); + return Optional.empty(); + } + } + private Long getIntervalInSecond(final ConnectionScheduleDataBasicSchedule schedule) { return getSecondsInUnit(schedule.getTimeUnit()) * schedule.getUnits(); } diff --git a/airbyte-workers/src/test/java/io/airbyte/workers/temporal/scheduling/ConnectionNotificationWorkflowTest.java b/airbyte-workers/src/test/java/io/airbyte/workers/temporal/scheduling/ConnectionNotificationWorkflowTest.java index 97b2dd5a416a..c6fa40328443 100644 --- a/airbyte-workers/src/test/java/io/airbyte/workers/temporal/scheduling/ConnectionNotificationWorkflowTest.java +++ b/airbyte-workers/src/test/java/io/airbyte/workers/temporal/scheduling/ConnectionNotificationWorkflowTest.java @@ -13,7 +13,6 @@ import io.airbyte.api.client.invoker.generated.ApiException; import io.airbyte.commons.temporal.scheduling.ConnectionNotificationWorkflow; import io.airbyte.config.SlackNotificationConfiguration; -import io.airbyte.config.StandardSync; import io.airbyte.config.persistence.ConfigNotFoundException; import io.airbyte.notification.SlackNotificationClient; import io.airbyte.validation.json.JsonValidationException; @@ -102,7 +101,7 @@ void sendSchemaChangeNotificationNonBreakingChangeTest() final UUID connectionId = UUID.randomUUID(); - when(mConfigFetchActivity.getStandardSync(connectionId)).thenReturn(new StandardSync().withBreakingChange(false)); + when(mConfigFetchActivity.getBreakingChange(connectionId)).thenReturn(Optional.of(false)); workflow.sendSchemaChangeNotification(connectionId); verify(mNotifySchemaChangeActivity, times(1)).notifySchemaChange(any(SlackNotificationClient.class), any(UUID.class), any(boolean.class)); diff --git a/airbyte-workers/src/test/java/io/airbyte/workers/temporal/scheduling/activities/ConfigFetchActivityTest.java b/airbyte-workers/src/test/java/io/airbyte/workers/temporal/scheduling/activities/ConfigFetchActivityTest.java index e6cb60e04d4a..320df0ac0af4 100644 --- a/airbyte-workers/src/test/java/io/airbyte/workers/temporal/scheduling/activities/ConfigFetchActivityTest.java +++ b/airbyte-workers/src/test/java/io/airbyte/workers/temporal/scheduling/activities/ConfigFetchActivityTest.java @@ -8,6 +8,7 @@ import static org.mockito.Mockito.when; import io.airbyte.api.client.generated.ConnectionApi; +import io.airbyte.api.client.generated.WorkspaceApi; import io.airbyte.api.client.invoker.generated.ApiException; import io.airbyte.api.client.model.generated.ConnectionRead; import io.airbyte.api.client.model.generated.ConnectionSchedule; @@ -17,10 +18,9 @@ import io.airbyte.api.client.model.generated.ConnectionScheduleDataCron; import io.airbyte.api.client.model.generated.ConnectionScheduleType; import io.airbyte.api.client.model.generated.ConnectionStatus; +import io.airbyte.api.client.model.generated.WorkspaceRead; import io.airbyte.config.persistence.ConfigNotFoundException; -import io.airbyte.config.persistence.ConfigRepository; import io.airbyte.persistence.job.JobPersistence; -import io.airbyte.persistence.job.WorkspaceHelper; import io.airbyte.persistence.job.models.Job; import io.airbyte.validation.json.JsonValidationException; import io.airbyte.workers.temporal.scheduling.activities.ConfigFetchActivity.ScheduleRetrieverInput; @@ -46,15 +46,11 @@ class ConfigFetchActivityTest { private static final Integer SYNC_JOB_MAX_ATTEMPTS = 3; - @Mock - private ConfigRepository mConfigRepository; - @Mock private JobPersistence mJobPersistence; @Mock - private WorkspaceHelper mWorkspaceHelper; - + private WorkspaceApi mWorkspaceApi; @Mock private Job mJob; @@ -107,7 +103,7 @@ class ConfigFetchActivityTest { @BeforeEach void setup() { configFetchActivity = - new ConfigFetchActivityImpl(mConfigRepository, mJobPersistence, mWorkspaceHelper, SYNC_JOB_MAX_ATTEMPTS, + new ConfigFetchActivityImpl(mJobPersistence, mWorkspaceApi, SYNC_JOB_MAX_ATTEMPTS, () -> Instant.now().getEpochSecond(), mConnectionApi); } @@ -177,7 +173,7 @@ void testDeleted() throws ApiException { @DisplayName("Test we will wait the required amount of time with legacy config") void testWait() throws IOException, JsonValidationException, ConfigNotFoundException, ApiException { configFetchActivity = - new ConfigFetchActivityImpl(mConfigRepository, mJobPersistence, SYNC_JOB_MAX_ATTEMPTS, () -> 60L * 3, mConnectionApi); + new ConfigFetchActivityImpl(mJobPersistence, mWorkspaceApi, SYNC_JOB_MAX_ATTEMPTS, () -> 60L * 3, mConnectionApi); when(mJob.getStartedAtInSecond()) .thenReturn(Optional.of(60L)); @@ -200,7 +196,7 @@ void testWait() throws IOException, JsonValidationException, ConfigNotFoundExcep @DisplayName("Test we will not wait if we are late in the legacy schedule schema") void testNotWaitIfLate() throws IOException, ApiException { configFetchActivity = - new ConfigFetchActivityImpl(mConfigRepository, mJobPersistence, SYNC_JOB_MAX_ATTEMPTS, () -> 60L * 10, mConnectionApi); + new ConfigFetchActivityImpl(mJobPersistence, mWorkspaceApi, SYNC_JOB_MAX_ATTEMPTS, () -> 60L * 10, mConnectionApi); when(mJob.getStartedAtInSecond()) .thenReturn(Optional.of(60L)); @@ -255,7 +251,7 @@ void testBasicScheduleTypeFirstRun() throws IOException, ApiException { @Test @DisplayName("Test that we will wait the required amount of time with a BASIC_SCHEDULE type on a subsequent run") void testBasicScheduleSubsequentRun() throws IOException, ApiException { - configFetchActivity = new ConfigFetchActivityImpl(mConfigRepository, mJobPersistence, SYNC_JOB_MAX_ATTEMPTS, () -> 60L * 3, mConnectionApi); + configFetchActivity = new ConfigFetchActivityImpl(mJobPersistence, mWorkspaceApi, SYNC_JOB_MAX_ATTEMPTS, () -> 60L * 3, mConnectionApi); when(mJob.getStartedAtInSecond()) .thenReturn(Optional.of(60L)); @@ -283,10 +279,10 @@ void testCronScheduleSubsequentRun() throws IOException, JsonValidationException mockRightNow.set(Calendar.SECOND, 0); mockRightNow.set(Calendar.MILLISECOND, 0); - when(mWorkspaceHelper.getWorkspaceForConnectionId(any())).thenReturn(UUID.randomUUID()); + when(mWorkspaceApi.getWorkspaceByConnectionId(any())).thenReturn(new WorkspaceRead().workspaceId(UUID.randomUUID())); configFetchActivity = - new ConfigFetchActivityImpl(mConfigRepository, mJobPersistence, mWorkspaceHelper, SYNC_JOB_MAX_ATTEMPTS, + new ConfigFetchActivityImpl(mJobPersistence, mWorkspaceApi, SYNC_JOB_MAX_ATTEMPTS, () -> mockRightNow.getTimeInMillis() / 1000L, mConnectionApi); when(mJobPersistence.getLastReplicationJob(connectionId)) @@ -312,10 +308,10 @@ void testCronScheduleMinimumInterval() throws IOException, JsonValidationExcepti mockRightNow.set(Calendar.SECOND, 0); mockRightNow.set(Calendar.MILLISECOND, 0); - when(mWorkspaceHelper.getWorkspaceForConnectionId(any())).thenReturn(UUID.randomUUID()); + when(mWorkspaceApi.getWorkspaceByConnectionId(any())).thenReturn(new WorkspaceRead().workspaceId(UUID.randomUUID())); configFetchActivity = - new ConfigFetchActivityImpl(mConfigRepository, mJobPersistence, mWorkspaceHelper, SYNC_JOB_MAX_ATTEMPTS, + new ConfigFetchActivityImpl(mJobPersistence, mWorkspaceApi, SYNC_JOB_MAX_ATTEMPTS, () -> mockRightNow.getTimeInMillis() / 1000L, mConnectionApi); when(mJob.getStartedAtInSecond()).thenReturn(Optional.of(mockRightNow.getTimeInMillis() / 1000L)); @@ -342,10 +338,11 @@ void testCronSchedulingNoise() throws IOException, JsonValidationException, Conf mockRightNow.set(Calendar.SECOND, 0); mockRightNow.set(Calendar.MILLISECOND, 0); - when(mWorkspaceHelper.getWorkspaceForConnectionId(any())).thenReturn(UUID.fromString("226edbc1-4a9c-4401-95a9-90435d667d9d")); + when(mWorkspaceApi.getWorkspaceByConnectionId(any())) + .thenReturn(new WorkspaceRead().workspaceId(UUID.fromString("226edbc1-4a9c-4401-95a9-90435d667d9d"))); configFetchActivity = - new ConfigFetchActivityImpl(mConfigRepository, mJobPersistence, mWorkspaceHelper, SYNC_JOB_MAX_ATTEMPTS, + new ConfigFetchActivityImpl(mJobPersistence, mWorkspaceApi, SYNC_JOB_MAX_ATTEMPTS, () -> mockRightNow.getTimeInMillis() / 1000L, mConnectionApi); when(mJob.getStartedAtInSecond()).thenReturn(Optional.of(mockRightNow.getTimeInMillis() / 1000L)); @@ -371,7 +368,7 @@ class TestGetMaxAttempt { void testGetMaxAttempt() { final int maxAttempt = 15031990; configFetchActivity = - new ConfigFetchActivityImpl(mConfigRepository, mJobPersistence, maxAttempt, () -> Instant.now().getEpochSecond(), mConnectionApi); + new ConfigFetchActivityImpl(mJobPersistence, mWorkspaceApi, maxAttempt, () -> Instant.now().getEpochSecond(), mConnectionApi); Assertions.assertThat(configFetchActivity.getMaxAttempt().getMaxAttempt()) .isEqualTo(maxAttempt); } From ca10a01669feba4bc763f08d4523dd3beaede2ad Mon Sep 17 00:00:00 2001 From: Greg Solovyev Date: Thu, 5 Jan 2023 15:32:08 -0800 Subject: [PATCH 008/170] Publish new version of destination-redshift (#21083) * Update changelog * auto-bump connector version Co-authored-by: Octavia Squidington III --- .../init/src/main/resources/seed/destination_definitions.yaml | 2 +- .../init/src/main/resources/seed/destination_specs.yaml | 2 +- docs/integrations/destinations/redshift.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/airbyte-config/init/src/main/resources/seed/destination_definitions.yaml b/airbyte-config/init/src/main/resources/seed/destination_definitions.yaml index 1bae1bcf37fb..22a3651376b9 100644 --- a/airbyte-config/init/src/main/resources/seed/destination_definitions.yaml +++ b/airbyte-config/init/src/main/resources/seed/destination_definitions.yaml @@ -287,7 +287,7 @@ - name: Redshift destinationDefinitionId: f7a7d195-377f-cf5b-70a5-be6b819019dc dockerRepository: airbyte/destination-redshift - dockerImageTag: 0.3.52 + dockerImageTag: 0.3.53 documentationUrl: https://docs.airbyte.com/integrations/destinations/redshift icon: redshift.svg normalizationConfig: diff --git a/airbyte-config/init/src/main/resources/seed/destination_specs.yaml b/airbyte-config/init/src/main/resources/seed/destination_specs.yaml index ba053a762ec4..faf403561ba4 100644 --- a/airbyte-config/init/src/main/resources/seed/destination_specs.yaml +++ b/airbyte-config/init/src/main/resources/seed/destination_specs.yaml @@ -5083,7 +5083,7 @@ supported_destination_sync_modes: - "overwrite" - "append" -- dockerImage: "airbyte/destination-redshift:0.3.52" +- dockerImage: "airbyte/destination-redshift:0.3.53" spec: documentationUrl: "https://docs.airbyte.com/integrations/destinations/redshift" connectionSpecification: diff --git a/docs/integrations/destinations/redshift.md b/docs/integrations/destinations/redshift.md index 7a1bd583ac2f..e20eea1d5037 100644 --- a/docs/integrations/destinations/redshift.md +++ b/docs/integrations/destinations/redshift.md @@ -141,7 +141,7 @@ Each stream will be output into its own raw table in Redshift. Each table will c | Version | Date | Pull Request | Subject | |:--------|:-----------|:-----------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| 0.3.53 | 2023-01-03 | [\#17273](https://github.com/airbytehq/airbyte/pull/17273) | Fixed handling of arrays in SUPER maximum size check | +| 0.3.53 | 2023-01-03 | [\#17273](https://github.com/airbytehq/airbyte/pull/17273) | Flatten JSON arrays to fix maximum size check for SUPER field | | 0.3.52 | 2022-12-30 | [\#20879](https://github.com/airbytehq/airbyte/pull/20879) | Added configurable parameter for number of file buffers | | 0.3.51 | 2022-10-26 | [\#18434](https://github.com/airbytehq/airbyte/pull/18434) | Fix empty S3 bucket path handling | | 0.3.50 | 2022-09-14 | [\#15668](https://github.com/airbytehq/airbyte/pull/15668) | Wrap logs in AirbyteLogMessage | From 6f8e57828d5839a76d9b9981cda08289e472b02f Mon Sep 17 00:00:00 2001 From: Davin Chia Date: Thu, 5 Jan 2023 16:32:19 -0800 Subject: [PATCH 009/170] Progress Bar Read APIs (#20937) Follow up PR to #20787 . Make stats available to the read apis so these are available to the webapp. After this, all that is left is writing these stats as the job progresses. Add the required logic in JobHistoryHandler.java. Took the chance to also rename our internal Attempt models field from id to attemptNumber to better reflect that the field stores not the row's database id, but the job's attempt number. Most of the files changes here are due to that rename. --- .../job/DefaultJobPersistence.java | 70 ++++++++- .../persistence/job/JobPersistence.java | 15 ++ .../persistence/job/models/Attempt.java | 16 +- .../job/DefaultJobPersistenceTest.java | 140 +++++++++++++----- .../persistence/job/models/AttemptTest.java | 2 +- .../persistence/job/models/JobTest.java | 2 +- .../server/converters/JobConverter.java | 2 +- .../server/handlers/JobHistoryHandler.java | 69 ++++++++- .../server/converters/JobConverterTest.java | 6 +- .../handlers/JobHistoryHandlerTest.java | 82 ++++++---- ...obCreationAndStatusUpdateActivityImpl.java | 5 +- 11 files changed, 322 insertions(+), 87 deletions(-) diff --git a/airbyte-persistence/job-persistence/src/main/java/io/airbyte/persistence/job/DefaultJobPersistence.java b/airbyte-persistence/job-persistence/src/main/java/io/airbyte/persistence/job/DefaultJobPersistence.java index 1c5dd8253cdb..d7485ee70644 100644 --- a/airbyte-persistence/job-persistence/src/main/java/io/airbyte/persistence/job/DefaultJobPersistence.java +++ b/airbyte-persistence/job-persistence/src/main/java/io/airbyte/persistence/job/DefaultJobPersistence.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.collect.UnmodifiableIterator; import io.airbyte.commons.enums.Enums; @@ -72,6 +73,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; import org.jooq.DSLContext; import org.jooq.Field; import org.jooq.InsertValuesStepN; @@ -511,6 +513,72 @@ public AttemptStats getAttemptStats(final long jobId, final int attemptNumber) t }); } + @Override + public Map getAttemptStats(final List jobIds) throws IOException { + final var jobIdsStr = StringUtils.join(jobIds, ','); + return jobDatabase.query(ctx -> { + // Instead of one massive join query, separate this query into two queries for better readability + // for now. + // We can combine the queries at a later date if this still proves to be not efficient enough. + final Map attemptStats = hydrateSyncStats(jobIdsStr, ctx); + hydrateStreamStats(jobIdsStr, ctx, attemptStats); + return attemptStats; + }); + } + + private static Map hydrateSyncStats(final String jobIdsStr, final DSLContext ctx) { + final var attemptStats = new HashMap(); + final var syncResults = ctx.fetch( + "SELECT atmpt.attempt_number, atmpt.job_id," + + "stats.estimated_bytes, stats.estimated_records, stats.bytes_emitted, stats.records_emitted " + + "FROM sync_stats stats " + + "INNER JOIN attempts atmpt ON stats.attempt_id = atmpt.id " + + "WHERE job_id IN ( " + jobIdsStr + ");"); + syncResults.forEach(r -> { + final var key = new JobAttemptPair(r.get(ATTEMPTS.JOB_ID), r.get(ATTEMPTS.ATTEMPT_NUMBER)); + final var syncStats = new SyncStats() + .withBytesEmitted(r.get(SYNC_STATS.BYTES_EMITTED)) + .withRecordsEmitted(r.get(SYNC_STATS.RECORDS_EMITTED)) + .withEstimatedRecords(r.get(SYNC_STATS.ESTIMATED_RECORDS)) + .withEstimatedBytes(r.get(SYNC_STATS.ESTIMATED_BYTES)); + attemptStats.put(key, new AttemptStats(syncStats, Lists.newArrayList())); + }); + return attemptStats; + } + + /** + * This method needed to be called after + * {@link DefaultJobPersistence#hydrateSyncStats(String, DSLContext)} as it assumes hydrateSyncStats + * has prepopulated the map. + */ + private static void hydrateStreamStats(final String jobIdsStr, final DSLContext ctx, final Map attemptStats) { + final var streamResults = ctx.fetch( + "SELECT atmpt.attempt_number, atmpt.job_id, " + + "stats.stream_name, stats.stream_namespace, stats.estimated_bytes, stats.estimated_records, stats.bytes_emitted, stats.records_emitted " + + "FROM stream_stats stats " + + "INNER JOIN attempts atmpt ON atmpt.id = stats.attempt_id " + + "WHERE attempt_id IN " + + "( SELECT id FROM attempts WHERE job_id IN ( " + jobIdsStr + "));"); + + streamResults.forEach(r -> { + final var streamSyncStats = new StreamSyncStats() + .withStreamNamespace(r.get(STREAM_STATS.STREAM_NAMESPACE)) + .withStreamName(r.get(STREAM_STATS.STREAM_NAME)) + .withStats(new SyncStats() + .withBytesEmitted(r.get(STREAM_STATS.BYTES_EMITTED)) + .withRecordsEmitted(r.get(STREAM_STATS.RECORDS_EMITTED)) + .withEstimatedRecords(r.get(STREAM_STATS.ESTIMATED_RECORDS)) + .withEstimatedBytes(r.get(STREAM_STATS.ESTIMATED_BYTES))); + + final var key = new JobAttemptPair(r.get(ATTEMPTS.JOB_ID), r.get(ATTEMPTS.ATTEMPT_NUMBER)); + if (!attemptStats.containsKey(key)) { + LOGGER.error("{} stream stats entry does not have a corresponding sync stats entry. This suggest the database is in a bad state.", key); + return; + } + attemptStats.get(key).perStreamStats().add(streamSyncStats); + }); + } + @Override public List getNormalizationSummary(final long jobId, final int attemptNumber) throws IOException { return jobDatabase @@ -849,7 +917,7 @@ private static Job getJobFromRecord(final Record record) { private static Attempt getAttemptFromRecord(final Record record) { return new Attempt( - record.get(ATTEMPT_NUMBER, Long.class), + record.get(ATTEMPT_NUMBER, int.class), record.get(JOB_ID, Long.class), Path.of(record.get("log_path", String.class)), record.get("attempt_output", String.class) == null ? null : Jsons.deserialize(record.get("attempt_output", String.class), JobOutput.class), diff --git a/airbyte-persistence/job-persistence/src/main/java/io/airbyte/persistence/job/JobPersistence.java b/airbyte-persistence/job-persistence/src/main/java/io/airbyte/persistence/job/JobPersistence.java index 895382c40b20..da7b3a98474e 100644 --- a/airbyte-persistence/job-persistence/src/main/java/io/airbyte/persistence/job/JobPersistence.java +++ b/airbyte-persistence/job-persistence/src/main/java/io/airbyte/persistence/job/JobPersistence.java @@ -49,6 +49,8 @@ public interface JobPersistence { */ record AttemptStats(SyncStats combinedStats, List perStreamStats) {} + record JobAttemptPair(long id, int attemptNumber) {} + /** * Retrieve the combined and per stream stats for a single attempt. * @@ -57,6 +59,19 @@ record AttemptStats(SyncStats combinedStats, List perStreamStat */ AttemptStats getAttemptStats(long jobId, int attemptNumber) throws IOException; + /** + * Alternative method to retrieve combined and per stream stats per attempt for a list of jobs to + * avoid overloading the database with too many queries. + *

+ * This implementation is intended to utilise complex joins under the hood to reduce the potential + * N+1 database pattern. + * + * @param jobIds + * @return + * @throws IOException + */ + Map getAttemptStats(List jobIds) throws IOException; + List getNormalizationSummary(long jobId, int attemptNumber) throws IOException; Job getJob(long jobId) throws IOException; diff --git a/airbyte-persistence/job-persistence/src/main/java/io/airbyte/persistence/job/models/Attempt.java b/airbyte-persistence/job-persistence/src/main/java/io/airbyte/persistence/job/models/Attempt.java index 110deaecab7b..a3dc08b076d2 100644 --- a/airbyte-persistence/job-persistence/src/main/java/io/airbyte/persistence/job/models/Attempt.java +++ b/airbyte-persistence/job-persistence/src/main/java/io/airbyte/persistence/job/models/Attempt.java @@ -13,7 +13,7 @@ public class Attempt { - private final long id; + private final int attemptNumber; private final long jobId; private final JobOutput output; private final AttemptStatus status; @@ -23,7 +23,7 @@ public class Attempt { private final long createdAtInSecond; private final Long endedAtInSecond; - public Attempt(final long id, + public Attempt(final int attemptNumber, final long jobId, final Path logPath, final @Nullable JobOutput output, @@ -32,7 +32,7 @@ public Attempt(final long id, final long createdAtInSecond, final long updatedAtInSecond, final @Nullable Long endedAtInSecond) { - this.id = id; + this.attemptNumber = attemptNumber; this.jobId = jobId; this.output = output; this.status = status; @@ -43,8 +43,8 @@ public Attempt(final long id, this.endedAtInSecond = endedAtInSecond; } - public long getId() { - return id; + public int getAttemptNumber() { + return attemptNumber; } public long getJobId() { @@ -92,7 +92,7 @@ public boolean equals(final Object o) { return false; } final Attempt attempt = (Attempt) o; - return id == attempt.id && + return attemptNumber == attempt.attemptNumber && jobId == attempt.jobId && updatedAtInSecond == attempt.updatedAtInSecond && createdAtInSecond == attempt.createdAtInSecond && @@ -105,13 +105,13 @@ public boolean equals(final Object o) { @Override public int hashCode() { - return Objects.hash(id, jobId, output, status, failureSummary, logPath, updatedAtInSecond, createdAtInSecond, endedAtInSecond); + return Objects.hash(attemptNumber, jobId, output, status, failureSummary, logPath, updatedAtInSecond, createdAtInSecond, endedAtInSecond); } @Override public String toString() { return "Attempt{" + - "id=" + id + + "id=" + attemptNumber + ", jobId=" + jobId + ", output=" + output + ", status=" + status + diff --git a/airbyte-persistence/job-persistence/src/test/java/io/airbyte/persistence/job/DefaultJobPersistenceTest.java b/airbyte-persistence/job-persistence/src/test/java/io/airbyte/persistence/job/DefaultJobPersistenceTest.java index 0b48aaf036c4..bae6a9435222 100644 --- a/airbyte-persistence/job-persistence/src/test/java/io/airbyte/persistence/job/DefaultJobPersistenceTest.java +++ b/airbyte-persistence/job-persistence/src/test/java/io/airbyte/persistence/job/DefaultJobPersistenceTest.java @@ -14,6 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -48,6 +49,7 @@ import io.airbyte.db.instance.jobs.JobsDatabaseSchema; import io.airbyte.db.instance.test.TestDatabaseProviders; import io.airbyte.persistence.job.JobPersistence.AttemptStats; +import io.airbyte.persistence.job.JobPersistence.JobAttemptPair; import io.airbyte.persistence.job.models.Attempt; import io.airbyte.persistence.job.models.AttemptStatus; import io.airbyte.persistence.job.models.AttemptWithJobInfo; @@ -142,7 +144,7 @@ static void dbDown() { container.close(); } - private static Attempt createAttempt(final long id, final long jobId, final AttemptStatus status, final Path logPath) { + private static Attempt createAttempt(final int id, final long jobId, final AttemptStatus status, final Path logPath) { return new Attempt( id, jobId, @@ -155,7 +157,7 @@ private static Attempt createAttempt(final long id, final long jobId, final Atte NOW.getEpochSecond()); } - private static Attempt createUnfinishedAttempt(final long id, final long jobId, final AttemptStatus status, final Path logPath) { + private static Attempt createUnfinishedAttempt(final int id, final long jobId, final AttemptStatus status, final Path logPath) { return new Attempt( id, jobId, @@ -238,7 +240,7 @@ void testCompleteAttemptFailed() throws IOException { jobId, SPEC_JOB_CONFIG, JobStatus.INCOMPLETE, - Lists.newArrayList(createAttempt(0L, jobId, AttemptStatus.FAILED, LOG_PATH)), + Lists.newArrayList(createAttempt(0, jobId, AttemptStatus.FAILED, LOG_PATH)), NOW.getEpochSecond()); assertEquals(expected, actual); } @@ -256,7 +258,7 @@ void testCompleteAttemptSuccess() throws IOException { jobId, SPEC_JOB_CONFIG, JobStatus.SUCCEEDED, - Lists.newArrayList(createAttempt(0L, jobId, AttemptStatus.SUCCEEDED, LOG_PATH)), + Lists.newArrayList(createAttempt(0, jobId, AttemptStatus.SUCCEEDED, LOG_PATH)), NOW.getEpochSecond()); assertEquals(expected, actual); } @@ -326,8 +328,8 @@ void testWriteAttemptFailureSummary() throws IOException { } @Nested - @DisplayName("Test writing in progress stats") - class WriteStats { + @DisplayName("Stats Related Tests") + class Stats { @Test @DisplayName("Writing stats the first time should only write record and bytes information correctly") @@ -455,6 +457,69 @@ void testWriteNullNamespace() throws IOException { assertEquals(streamStats, actStreamStats); } + @Test + @DisplayName("Writing multiple stats a stream with null namespace should write correctly without exceptions") + void testGetStatsNoResult() throws IOException { + final long jobId = jobPersistence.enqueueJob(SCOPE, SPEC_JOB_CONFIG).orElseThrow(); + final int attemptNumber = jobPersistence.createAttempt(jobId, LOG_PATH); + + final AttemptStats stats = jobPersistence.getAttemptStats(jobId, attemptNumber); + assertNull(stats.combinedStats()); + assertEquals(0, stats.perStreamStats().size()); + + } + + @Test + @DisplayName("Retrieving all attempts stats for a job should return the right information") + void testGetMultipleStats() throws IOException { + final long jobOneId = jobPersistence.enqueueJob(SCOPE, SPEC_JOB_CONFIG).orElseThrow(); + final int jobOneAttemptNumberOne = jobPersistence.createAttempt(jobOneId, LOG_PATH); + + // First write for first attempt. + var streamStats = List.of( + new StreamSyncStats().withStreamName("name1") + .withStats(new SyncStats().withBytesEmitted(500L).withRecordsEmitted(500L).withEstimatedBytes(10000L).withEstimatedRecords(2000L))); + jobPersistence.writeStats(jobOneId, jobOneAttemptNumberOne, 1000, 1000, 1000, 1000, streamStats); + + // Second write for first attempt. This is the record that should be returned. + when(timeSupplier.get()).thenReturn(Instant.now()); + streamStats = List.of( + new StreamSyncStats().withStreamName("name1") + .withStats(new SyncStats().withBytesEmitted(1000L).withRecordsEmitted(1000L).withEstimatedBytes(10000L).withEstimatedRecords(2000L))); + jobPersistence.writeStats(jobOneId, jobOneAttemptNumberOne, 2000, 2000, 2000, 2000, streamStats); + jobPersistence.failAttempt(jobOneId, jobOneAttemptNumberOne); + + // Second attempt for first job. + final int jobOneAttemptNumberTwo = jobPersistence.createAttempt(jobOneId, LOG_PATH); + jobPersistence.writeStats(jobOneId, jobOneAttemptNumberTwo, 1000, 1000, 1000, 1000, streamStats); + + // First attempt for second job. + final long jobTwoId = jobPersistence.enqueueJob(SCOPE, SPEC_JOB_CONFIG).orElseThrow(); + final int jobTwoAttemptNumberOne = jobPersistence.createAttempt(jobTwoId, LOG_PATH); + jobPersistence.writeStats(jobTwoId, jobTwoAttemptNumberOne, 1000, 1000, 1000, 1000, streamStats); + + final var stats = jobPersistence.getAttemptStats(List.of(jobOneId, jobTwoId)); + final var exp = Map.of( + new JobAttemptPair(jobOneId, jobOneAttemptNumberOne), + new AttemptStats( + new SyncStats().withRecordsEmitted(2000L).withBytesEmitted(2000L).withEstimatedBytes(2000L).withEstimatedRecords(2000L), + List.of(new StreamSyncStats().withStreamName("name1").withStats( + new SyncStats().withEstimatedBytes(10000L).withEstimatedRecords(2000L).withBytesEmitted(1000L).withRecordsEmitted(1000L)))), + new JobAttemptPair(jobOneId, jobOneAttemptNumberTwo), + new AttemptStats( + new SyncStats().withRecordsEmitted(1000L).withBytesEmitted(1000L).withEstimatedBytes(1000L).withEstimatedRecords(1000L), + List.of(new StreamSyncStats().withStreamName("name1").withStats( + new SyncStats().withEstimatedBytes(10000L).withEstimatedRecords(2000L).withBytesEmitted(1000L).withRecordsEmitted(1000L)))), + new JobAttemptPair(jobTwoId, jobTwoAttemptNumberOne), + new AttemptStats( + new SyncStats().withRecordsEmitted(1000L).withBytesEmitted(1000L).withEstimatedBytes(1000L).withEstimatedRecords(1000L), + List.of(new StreamSyncStats().withStreamName("name1").withStats( + new SyncStats().withEstimatedBytes(10000L).withEstimatedRecords(2000L).withBytesEmitted(1000L).withRecordsEmitted(1000L))))); + + assertEquals(exp, stats); + + } + } @Test @@ -471,8 +536,8 @@ void testGetLastSyncJobWithMultipleAttempts() throws IOException { SYNC_JOB_CONFIG, JobStatus.INCOMPLETE, Lists.newArrayList( - createAttempt(0L, jobId, AttemptStatus.FAILED, LOG_PATH), - createAttempt(1L, jobId, AttemptStatus.FAILED, LOG_PATH)), + createAttempt(0, jobId, AttemptStatus.FAILED, LOG_PATH), + createAttempt(1, jobId, AttemptStatus.FAILED, LOG_PATH)), NOW.getEpochSecond()); assertEquals(Optional.of(expected), actual); @@ -514,15 +579,14 @@ void testExportImport() throws IOException, SQLException { jobPersistence.importDatabase("test", outputStreams); final List actualList = jobPersistence.listJobs(SPEC_JOB_CONFIG.getConfigType(), CONNECTION_ID.toString(), 9999, 0); - final Job actual = actualList.get(0); final Job expected = createJob( jobId, SPEC_JOB_CONFIG, JobStatus.SUCCEEDED, Lists.newArrayList( - createAttempt(0L, jobId, AttemptStatus.FAILED, LOG_PATH), - createAttempt(1L, jobId, AttemptStatus.SUCCEEDED, secondAttemptLogPath)), + createAttempt(0, jobId, AttemptStatus.FAILED, LOG_PATH), + createAttempt(1, jobId, AttemptStatus.SUCCEEDED, secondAttemptLogPath)), NOW.getEpochSecond()); assertEquals(1, actualList.size()); @@ -557,8 +621,8 @@ void testListJobsWithTimestamp() throws IOException { assertEquals(jobs.size(), 1); assertEquals(jobs.get(0).getId(), syncJobId); assertEquals(jobs.get(0).getAttempts().size(), 2); - assertEquals(jobs.get(0).getAttempts().get(0).getId(), 0); - assertEquals(jobs.get(0).getAttempts().get(1).getId(), 1); + assertEquals(jobs.get(0).getAttempts().get(0).getAttemptNumber(), 0); + assertEquals(jobs.get(0).getAttempts().get(1).getAttemptNumber(), 1); final Path syncJobThirdAttemptLogPath = LOG_PATH.resolve("3"); final int syncJobAttemptNumber2 = jobPersistence.createAttempt(syncJobId, syncJobThirdAttemptLogPath); @@ -578,12 +642,12 @@ void testListJobsWithTimestamp() throws IOException { assertEquals(secondQueryJobs.size(), 2); assertEquals(secondQueryJobs.get(0).getId(), syncJobId); assertEquals(secondQueryJobs.get(0).getAttempts().size(), 1); - assertEquals(secondQueryJobs.get(0).getAttempts().get(0).getId(), 2); + assertEquals(secondQueryJobs.get(0).getAttempts().get(0).getAttemptNumber(), 2); assertEquals(secondQueryJobs.get(1).getId(), newSyncJobId); assertEquals(secondQueryJobs.get(1).getAttempts().size(), 2); - assertEquals(secondQueryJobs.get(1).getAttempts().get(0).getId(), 0); - assertEquals(secondQueryJobs.get(1).getAttempts().get(1).getId(), 1); + assertEquals(secondQueryJobs.get(1).getAttempts().get(0).getAttemptNumber(), 0); + assertEquals(secondQueryJobs.get(1).getAttempts().get(1).getAttemptNumber(), 1); Long maxEndedAtTimestampAfterSecondQuery = -1L; for (final Job c : secondQueryJobs) { @@ -628,35 +692,35 @@ void testListAttemptsWithJobInfo() throws IOException { assertEquals(6, allAttempts.size()); assertEquals(job1, allAttempts.get(0).getJobInfo().getId()); - assertEquals(job1Attempt1, allAttempts.get(0).getAttempt().getId()); + assertEquals(job1Attempt1, allAttempts.get(0).getAttempt().getAttemptNumber()); assertEquals(job2, allAttempts.get(1).getJobInfo().getId()); - assertEquals(job2Attempt1, allAttempts.get(1).getAttempt().getId()); + assertEquals(job2Attempt1, allAttempts.get(1).getAttempt().getAttemptNumber()); assertEquals(job2, allAttempts.get(2).getJobInfo().getId()); - assertEquals(job2Attempt2, allAttempts.get(2).getAttempt().getId()); + assertEquals(job2Attempt2, allAttempts.get(2).getAttempt().getAttemptNumber()); assertEquals(job1, allAttempts.get(3).getJobInfo().getId()); - assertEquals(job1Attempt2, allAttempts.get(3).getAttempt().getId()); + assertEquals(job1Attempt2, allAttempts.get(3).getAttempt().getAttemptNumber()); assertEquals(job1, allAttempts.get(4).getJobInfo().getId()); - assertEquals(job1Attempt3, allAttempts.get(4).getAttempt().getId()); + assertEquals(job1Attempt3, allAttempts.get(4).getAttempt().getAttemptNumber()); assertEquals(job2, allAttempts.get(5).getJobInfo().getId()); - assertEquals(job2Attempt3, allAttempts.get(5).getAttempt().getId()); + assertEquals(job2Attempt3, allAttempts.get(5).getAttempt().getAttemptNumber()); final List attemptsAfterTimestamp = jobPersistence.listAttemptsWithJobInfo(ConfigType.SYNC, Instant.ofEpochSecond(allAttempts.get(2).getAttempt().getEndedAtInSecond().orElseThrow())); assertEquals(3, attemptsAfterTimestamp.size()); assertEquals(job1, attemptsAfterTimestamp.get(0).getJobInfo().getId()); - assertEquals(job1Attempt2, attemptsAfterTimestamp.get(0).getAttempt().getId()); + assertEquals(job1Attempt2, attemptsAfterTimestamp.get(0).getAttempt().getAttemptNumber()); assertEquals(job1, attemptsAfterTimestamp.get(1).getJobInfo().getId()); - assertEquals(job1Attempt3, attemptsAfterTimestamp.get(1).getAttempt().getId()); + assertEquals(job1Attempt3, attemptsAfterTimestamp.get(1).getAttempt().getAttemptNumber()); assertEquals(job2, attemptsAfterTimestamp.get(2).getJobInfo().getId()); - assertEquals(job2Attempt3, attemptsAfterTimestamp.get(2).getAttempt().getId()); + assertEquals(job2Attempt3, attemptsAfterTimestamp.get(2).getAttempt().getAttemptNumber()); } private static Supplier incrementingSecondSupplier(final Instant startTime) { @@ -887,7 +951,7 @@ void testCreateAttempt() throws IOException { jobId, SPEC_JOB_CONFIG, JobStatus.RUNNING, - Lists.newArrayList(createUnfinishedAttempt(0L, jobId, AttemptStatus.RUNNING, LOG_PATH)), + Lists.newArrayList(createUnfinishedAttempt(0, jobId, AttemptStatus.RUNNING, LOG_PATH)), NOW.getEpochSecond()); assertEquals(expected, actual); } @@ -901,12 +965,12 @@ void testCreateAttemptAttemptId() throws IOException { final Job jobAfterOneAttempts = jobPersistence.getJob(jobId); assertEquals(0, attemptNumber1); - assertEquals(0, jobAfterOneAttempts.getAttempts().get(0).getId()); + assertEquals(0, jobAfterOneAttempts.getAttempts().get(0).getAttemptNumber()); final int attemptNumber2 = jobPersistence.createAttempt(jobId, LOG_PATH); final Job jobAfterTwoAttempts = jobPersistence.getJob(jobId); assertEquals(1, attemptNumber2); - assertEquals(Sets.newHashSet(0L, 1L), jobAfterTwoAttempts.getAttempts().stream().map(Attempt::getId).collect(Collectors.toSet())); + assertEquals(Sets.newHashSet(0, 1), jobAfterTwoAttempts.getAttempts().stream().map(Attempt::getAttemptNumber).collect(Collectors.toSet())); } @Test @@ -922,7 +986,7 @@ void testCreateAttemptWhileAttemptAlreadyRunning() throws IOException { jobId, SPEC_JOB_CONFIG, JobStatus.RUNNING, - Lists.newArrayList(createUnfinishedAttempt(0L, jobId, AttemptStatus.RUNNING, LOG_PATH)), + Lists.newArrayList(createUnfinishedAttempt(0, jobId, AttemptStatus.RUNNING, LOG_PATH)), NOW.getEpochSecond()); assertEquals(expected, actual); } @@ -941,7 +1005,7 @@ void testCreateAttemptTerminal() throws IOException { jobId, SPEC_JOB_CONFIG, JobStatus.SUCCEEDED, - Lists.newArrayList(createAttempt(0L, jobId, AttemptStatus.SUCCEEDED, LOG_PATH)), + Lists.newArrayList(createAttempt(0, jobId, AttemptStatus.SUCCEEDED, LOG_PATH)), NOW.getEpochSecond()); assertEquals(expected, actual); } @@ -1336,8 +1400,8 @@ void testGetNextJobWithMultipleAttempts() throws IOException { SPEC_JOB_CONFIG, JobStatus.PENDING, Lists.newArrayList( - createAttempt(0L, jobId, AttemptStatus.FAILED, LOG_PATH), - createAttempt(1L, jobId, AttemptStatus.FAILED, LOG_PATH)), + createAttempt(0, jobId, AttemptStatus.FAILED, LOG_PATH), + createAttempt(1, jobId, AttemptStatus.FAILED, LOG_PATH)), NOW.getEpochSecond()); assertEquals(Optional.of(expected), actual); @@ -1562,8 +1626,8 @@ void testListJobsWithMultipleAttempts() throws IOException { SPEC_JOB_CONFIG, JobStatus.SUCCEEDED, Lists.newArrayList( - createAttempt(0L, jobId, AttemptStatus.FAILED, LOG_PATH), - createAttempt(1L, jobId, AttemptStatus.SUCCEEDED, secondAttemptLogPath)), + createAttempt(0, jobId, AttemptStatus.FAILED, LOG_PATH), + createAttempt(1, jobId, AttemptStatus.SUCCEEDED, secondAttemptLogPath)), NOW.getEpochSecond()); assertEquals(1, actualList.size()); @@ -1682,7 +1746,7 @@ void testListJobsWithStatus() throws IOException { SPEC_JOB_CONFIG, JobStatus.INCOMPLETE, Lists.newArrayList( - createAttempt(0L, jobId, AttemptStatus.FAILED, LOG_PATH)), + createAttempt(0, jobId, AttemptStatus.FAILED, LOG_PATH)), NOW.getEpochSecond()); assertEquals(1, actualList.size()); @@ -1720,7 +1784,7 @@ void testListJobsWithStatusAndConfigType() throws IOException, InterruptedExcept SPEC_JOB_CONFIG, JobStatus.INCOMPLETE, Lists.newArrayList( - createAttempt(0L, failedSpecJobId, AttemptStatus.FAILED, LOG_PATH)), + createAttempt(0, failedSpecJobId, AttemptStatus.FAILED, LOG_PATH)), NOW.getEpochSecond(), SPEC_SCOPE); @@ -1757,12 +1821,12 @@ void testListJobsWithStatusesAndConfigTypesForConnection() throws IOException, I Set.of(ConfigType.SYNC, ConfigType.CHECK_CONNECTION_DESTINATION), Set.of(JobStatus.PENDING, JobStatus.SUCCEEDED)); final Job expectedDesiredJob1 = createJob(desiredJobId1, SYNC_JOB_CONFIG, JobStatus.SUCCEEDED, - Lists.newArrayList(createAttempt(0L, desiredJobId1, AttemptStatus.SUCCEEDED, LOG_PATH)), + Lists.newArrayList(createAttempt(0, desiredJobId1, AttemptStatus.SUCCEEDED, LOG_PATH)), NOW.getEpochSecond(), desiredConnectionId.toString()); final Job expectedDesiredJob2 = createJob(desiredJobId2, SYNC_JOB_CONFIG, JobStatus.PENDING, Lists.newArrayList(), NOW.getEpochSecond(), desiredConnectionId.toString()); final Job expectedDesiredJob3 = createJob(desiredJobId3, CHECK_JOB_CONFIG, JobStatus.SUCCEEDED, - Lists.newArrayList(createAttempt(0L, desiredJobId3, AttemptStatus.SUCCEEDED, LOG_PATH)), + Lists.newArrayList(createAttempt(0, desiredJobId3, AttemptStatus.SUCCEEDED, LOG_PATH)), NOW.getEpochSecond(), desiredConnectionId.toString()); final Job expectedDesiredJob4 = createJob(desiredJobId4, CHECK_JOB_CONFIG, JobStatus.PENDING, Lists.newArrayList(), NOW.getEpochSecond(), desiredConnectionId.toString()); diff --git a/airbyte-persistence/job-persistence/src/test/java/io/airbyte/persistence/job/models/AttemptTest.java b/airbyte-persistence/job-persistence/src/test/java/io/airbyte/persistence/job/models/AttemptTest.java index 0913a29ca734..badc1ac68d70 100644 --- a/airbyte-persistence/job-persistence/src/test/java/io/airbyte/persistence/job/models/AttemptTest.java +++ b/airbyte-persistence/job-persistence/src/test/java/io/airbyte/persistence/job/models/AttemptTest.java @@ -19,7 +19,7 @@ void testIsAttemptInTerminalState() { } private static Attempt attemptWithStatus(final AttemptStatus attemptStatus) { - return new Attempt(1L, 1L, null, null, attemptStatus, null, 0L, 0L, null); + return new Attempt(1, 1L, null, null, attemptStatus, null, 0L, 0L, null); } } diff --git a/airbyte-persistence/job-persistence/src/test/java/io/airbyte/persistence/job/models/JobTest.java b/airbyte-persistence/job-persistence/src/test/java/io/airbyte/persistence/job/models/JobTest.java index 3e10fa003d36..4cdb4f15403d 100644 --- a/airbyte-persistence/job-persistence/src/test/java/io/airbyte/persistence/job/models/JobTest.java +++ b/airbyte-persistence/job-persistence/src/test/java/io/airbyte/persistence/job/models/JobTest.java @@ -68,7 +68,7 @@ void testGetLastFailedAttempt() { final Job job = jobWithAttemptWithStatus(AttemptStatus.FAILED, AttemptStatus.FAILED); assertTrue(job.getLastFailedAttempt().isPresent()); - assertEquals(2, job.getLastFailedAttempt().get().getId()); + assertEquals(2, job.getLastFailedAttempt().get().getAttemptNumber()); } @Test diff --git a/airbyte-server/src/main/java/io/airbyte/server/converters/JobConverter.java b/airbyte-server/src/main/java/io/airbyte/server/converters/JobConverter.java index 52c28f3640f1..f478e85b90c0 100644 --- a/airbyte-server/src/main/java/io/airbyte/server/converters/JobConverter.java +++ b/airbyte-server/src/main/java/io/airbyte/server/converters/JobConverter.java @@ -137,7 +137,7 @@ public AttemptInfoRead getAttemptInfoRead(final Attempt attempt) { public static AttemptRead getAttemptRead(final Attempt attempt) { return new AttemptRead() - .id(attempt.getId()) + .id((long) attempt.getAttemptNumber()) .status(Enums.convertTo(attempt.getStatus(), AttemptStatus.class)) .bytesSynced(attempt.getOutput() // TODO (parker) remove after frontend switches to totalStats .map(JobOutput::getSync) diff --git a/airbyte-server/src/main/java/io/airbyte/server/handlers/JobHistoryHandler.java b/airbyte-server/src/main/java/io/airbyte/server/handlers/JobHistoryHandler.java index f61476a18600..752cb2e446ce 100644 --- a/airbyte-server/src/main/java/io/airbyte/server/handlers/JobHistoryHandler.java +++ b/airbyte-server/src/main/java/io/airbyte/server/handlers/JobHistoryHandler.java @@ -5,7 +5,11 @@ package io.airbyte.server.handlers; import com.google.common.base.Preconditions; +import io.airbyte.api.model.generated.AttemptInfoRead; import io.airbyte.api.model.generated.AttemptNormalizationStatusReadList; +import io.airbyte.api.model.generated.AttemptRead; +import io.airbyte.api.model.generated.AttemptStats; +import io.airbyte.api.model.generated.AttemptStreamStats; import io.airbyte.api.model.generated.ConnectionRead; import io.airbyte.api.model.generated.DestinationDefinitionIdRequestBody; import io.airbyte.api.model.generated.DestinationDefinitionRead; @@ -33,6 +37,7 @@ import io.airbyte.config.helpers.LogConfigs; import io.airbyte.config.persistence.ConfigNotFoundException; import io.airbyte.persistence.job.JobPersistence; +import io.airbyte.persistence.job.JobPersistence.JobAttemptPair; import io.airbyte.persistence.job.models.Job; import io.airbyte.persistence.job.models.JobStatus; import io.airbyte.server.converters.JobConverter; @@ -41,11 +46,14 @@ import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +@Slf4j public class JobHistoryHandler { private final ConnectionsHandler connectionsHandler; @@ -118,16 +126,61 @@ public JobReadList listJobsFor(final JobListRequestBody request) throws IOExcept (request.getPagination() != null && request.getPagination().getRowOffset() != null) ? request.getPagination().getRowOffset() : 0); } - final Long totalJobCount = jobPersistence.getJobCount(configTypes, configId); + final List jobReads = jobs.stream().map(JobConverter::getJobWithAttemptsRead).collect(Collectors.toList()); + final var jobIds = jobReads.stream().map(r -> r.getJob().getId()).toList(); + final Map stats = jobPersistence.getAttemptStats(jobIds); + for (final JobWithAttemptsRead jwar : jobReads) { + for (final AttemptRead a : jwar.getAttempts()) { + final var stat = stats.get(new JobAttemptPair(jwar.getJob().getId(), a.getId().intValue())); + if (stat == null) { + log.error("Missing stats for job {} attempt {}", jwar.getJob().getId(), a.getId().intValue()); + continue; + } - final List jobReads = jobs - .stream() - .map(JobConverter::getJobWithAttemptsRead) - .collect(Collectors.toList()); + hydrateWithStats(a, stat); + } + } + final Long totalJobCount = jobPersistence.getJobCount(configTypes, configId); return new JobReadList().jobs(jobReads).totalJobCount(totalJobCount); } + /** + * Retrieve stats for a given job id and attempt number and hydrate the api model with the retrieved + * information. + * + * @param jobId the job the attempt belongs to. Used as an index to retrieve stats. + * @param a the attempt to hydrate stats for. + * @throws IOException + */ + private void hydrateWithStats(final AttemptRead a, final JobPersistence.AttemptStats attemptStats) throws IOException { + a.setTotalStats(new AttemptStats()); + + final var combinedStats = attemptStats.combinedStats(); + if (combinedStats == null) { + // If overall stats are missing, assume stream stats are also missing, since overall stats are + // easier to produce than stream stats. Exit early. + return; + } + + a.getTotalStats() + .estimatedBytes(combinedStats.getEstimatedBytes()) + .estimatedRecords(combinedStats.getEstimatedRecords()) + .bytesEmitted(combinedStats.getBytesEmitted()) + .recordsEmitted(combinedStats.getRecordsEmitted()); + + final var streamStats = attemptStats.perStreamStats().stream().map(s -> new AttemptStreamStats() + .streamName(s.getStreamName()) + .streamNamespace(s.getStreamNamespace()) + .stats(new AttemptStats() + .bytesEmitted(s.getStats().getBytesEmitted()) + .recordsEmitted(s.getStats().getRecordsEmitted()) + .estimatedBytes(s.getStats().getEstimatedBytes()) + .estimatedRecords(s.getStats().getEstimatedRecords()))) + .collect(Collectors.toList()); + a.setStreamStats(streamStats); + } + public JobInfoRead getJobInfo(final JobIdRequestBody jobIdRequestBody) throws IOException { final Job job = jobPersistence.getJob(jobIdRequestBody.getId()); return jobConverter.getJobInfoRead(job); @@ -143,6 +196,12 @@ public JobDebugInfoRead getJobDebugInfo(final JobIdRequestBody jobIdRequestBody) final Job job = jobPersistence.getJob(jobIdRequestBody.getId()); final JobInfoRead jobinfoRead = jobConverter.getJobInfoRead(job); + for (final AttemptInfoRead a : jobinfoRead.getAttempts()) { + final int attemptNumber = a.getAttempt().getId().intValue(); + final var attemptStats = jobPersistence.getAttemptStats(job.getId(), attemptNumber); + hydrateWithStats(a.getAttempt(), attemptStats); + } + final JobDebugInfoRead jobDebugInfoRead = buildJobDebugInfoRead(jobinfoRead); if (temporalClient != null) { final UUID connectionId = UUID.fromString(job.getScope()); diff --git a/airbyte-server/src/test/java/io/airbyte/server/converters/JobConverterTest.java b/airbyte-server/src/test/java/io/airbyte/server/converters/JobConverterTest.java index fe1d084d92ce..f73ec6e62aad 100644 --- a/airbyte-server/src/test/java/io/airbyte/server/converters/JobConverterTest.java +++ b/airbyte-server/src/test/java/io/airbyte/server/converters/JobConverterTest.java @@ -68,7 +68,7 @@ class JobConverterTest { private static final long JOB_ID = 100L; - private static final long ATTEMPT_ID = 1002L; + private static final Integer ATTEMPT_NUMBER = 0; private static final String JOB_CONFIG_ID = "123"; private static final JobStatus JOB_STATUS = JobStatus.RUNNING; private static final AttemptStatus ATTEMPT_STATUS = AttemptStatus.RUNNING; @@ -124,7 +124,7 @@ class JobConverterTest { .updatedAt(CREATED_AT)) .attempts(Lists.newArrayList(new AttemptInfoRead() .attempt(new AttemptRead() - .id(ATTEMPT_ID) + .id((long) ATTEMPT_NUMBER) .status(io.airbyte.api.model.generated.AttemptStatus.RUNNING) .recordsSynced(RECORDS_EMITTED) .bytesSynced(BYTES_EMITTED) @@ -195,7 +195,7 @@ public void setUp() { when(job.getCreatedAtInSecond()).thenReturn(CREATED_AT); when(job.getUpdatedAtInSecond()).thenReturn(CREATED_AT); when(job.getAttempts()).thenReturn(Lists.newArrayList(attempt)); - when(attempt.getId()).thenReturn(ATTEMPT_ID); + when(attempt.getAttemptNumber()).thenReturn(ATTEMPT_NUMBER); when(attempt.getStatus()).thenReturn(ATTEMPT_STATUS); when(attempt.getOutput()).thenReturn(Optional.of(JOB_OUTPUT)); when(attempt.getLogPath()).thenReturn(LOG_PATH); diff --git a/airbyte-server/src/test/java/io/airbyte/server/handlers/JobHistoryHandlerTest.java b/airbyte-server/src/test/java/io/airbyte/server/handlers/JobHistoryHandlerTest.java index cb68ad2b8205..105e2fbb9c17 100644 --- a/airbyte-server/src/test/java/io/airbyte/server/handlers/JobHistoryHandlerTest.java +++ b/airbyte-server/src/test/java/io/airbyte/server/handlers/JobHistoryHandlerTest.java @@ -6,6 +6,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -14,6 +16,7 @@ import io.airbyte.api.model.generated.AttemptNormalizationStatusRead; import io.airbyte.api.model.generated.AttemptNormalizationStatusReadList; import io.airbyte.api.model.generated.AttemptRead; +import io.airbyte.api.model.generated.AttemptStreamStats; import io.airbyte.api.model.generated.ConnectionRead; import io.airbyte.api.model.generated.DestinationIdRequestBody; import io.airbyte.api.model.generated.DestinationRead; @@ -42,9 +45,13 @@ import io.airbyte.config.StandardDestinationDefinition; import io.airbyte.config.StandardSourceDefinition; import io.airbyte.config.StandardSync; +import io.airbyte.config.StreamSyncStats; +import io.airbyte.config.SyncStats; import io.airbyte.config.helpers.LogConfigs; import io.airbyte.config.persistence.ConfigNotFoundException; import io.airbyte.persistence.job.JobPersistence; +import io.airbyte.persistence.job.JobPersistence.AttemptStats; +import io.airbyte.persistence.job.JobPersistence.JobAttemptPair; import io.airbyte.persistence.job.models.Attempt; import io.airbyte.persistence.job.models.AttemptNormalizationStatus; import io.airbyte.persistence.job.models.AttemptStatus; @@ -63,6 +70,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -77,7 +85,7 @@ class JobHistoryHandlerTest { private static final long JOB_ID = 100L; - private static final long ATTEMPT_ID = 1002L; + private static final int ATTEMPT_NUMBER = 0; private static final String JOB_CONFIG_ID = "ef296385-6796-413f-ac1b-49c4caba3f2b"; private static final JobStatus JOB_STATUS = JobStatus.SUCCEEDED; private static final JobConfig.ConfigType CONFIG_TYPE = JobConfig.ConfigType.CHECK_CONNECTION_SOURCE; @@ -89,17 +97,24 @@ class JobHistoryHandlerTest { private static final LogRead EMPTY_LOG_READ = new LogRead().logLines(new ArrayList<>()); private static final long CREATED_AT = System.currentTimeMillis() / 1000; - private SourceRead sourceRead; - private ConnectionRead connectionRead; - private DestinationRead destinationRead; + private static final AttemptStats ATTEMPT_STATS = new AttemptStats(new SyncStats().withBytesEmitted(10L).withRecordsEmitted(10L), + List.of( + new StreamSyncStats().withStreamNamespace("ns1").withStreamName("stream1") + .withStats(new SyncStats().withRecordsEmitted(5L).withBytesEmitted(5L)), + new StreamSyncStats().withStreamName("stream2") + .withStats(new SyncStats().withRecordsEmitted(5L).withBytesEmitted(5L)))); + + private static final io.airbyte.api.model.generated.AttemptStats ATTEMPT_STATS_API = new io.airbyte.api.model.generated.AttemptStats() + .bytesEmitted(10L).recordsEmitted(10L); + + private static final List ATTEMPT_STREAM_STATS = List.of( + new AttemptStreamStats().streamNamespace("ns1").streamName("stream1") + .stats(new io.airbyte.api.model.generated.AttemptStats().recordsEmitted(5L).bytesEmitted(5L)), + new AttemptStreamStats().streamName("stream2").stats(new io.airbyte.api.model.generated.AttemptStats().recordsEmitted(5L).bytesEmitted(5L))); + private ConnectionsHandler connectionsHandler; private SourceHandler sourceHandler; private DestinationHandler destinationHandler; - private SourceDefinitionsHandler sourceDefinitionsHandler; - private DestinationDefinitionsHandler destinationDefinitionsHandler; - private StandardDestinationDefinition standardDestinationDefinition; - private StandardSourceDefinition standardSourceDefinition; - private AirbyteVersion airbyteVersion; private Job testJob; private Attempt testJobAttempt; private JobPersistence jobPersistence; @@ -134,7 +149,7 @@ private static List toAttemptInfoList(final List attem private static AttemptRead toAttemptRead(final Attempt a) { return new AttemptRead() - .id(a.getId()) + .id((long) a.getAttemptNumber()) .status(Enums.convertTo(a.getStatus(), io.airbyte.api.model.generated.AttemptStatus.class)) .createdAt(a.getCreatedAtInSecond()) .updatedAt(a.getUpdatedAtInSecond()) @@ -142,22 +157,22 @@ private static AttemptRead toAttemptRead(final Attempt a) { } private static Attempt createAttempt(final long jobId, final long timestamps, final AttemptStatus status) { - return new Attempt(ATTEMPT_ID, jobId, LOG_PATH, null, status, null, timestamps, timestamps, timestamps); + return new Attempt(ATTEMPT_NUMBER, jobId, LOG_PATH, null, status, null, timestamps, timestamps, timestamps); } @BeforeEach - void setUp() throws IOException, JsonValidationException, ConfigNotFoundException { + void setUp() { testJobAttempt = createAttempt(JOB_ID, CREATED_AT, AttemptStatus.SUCCEEDED); testJob = new Job(JOB_ID, JOB_CONFIG.getConfigType(), JOB_CONFIG_ID, JOB_CONFIG, ImmutableList.of(testJobAttempt), JOB_STATUS, null, CREATED_AT, CREATED_AT); connectionsHandler = mock(ConnectionsHandler.class); sourceHandler = mock(SourceHandler.class); - sourceDefinitionsHandler = mock(SourceDefinitionsHandler.class); destinationHandler = mock(DestinationHandler.class); - destinationDefinitionsHandler = mock(DestinationDefinitionsHandler.class); - airbyteVersion = mock(AirbyteVersion.class); jobPersistence = mock(JobPersistence.class); + final SourceDefinitionsHandler sourceDefinitionsHandler = mock(SourceDefinitionsHandler.class); + final DestinationDefinitionsHandler destinationDefinitionsHandler = mock(DestinationDefinitionsHandler.class); + final AirbyteVersion airbyteVersion = mock(AirbyteVersion.class); jobHistoryHandler = new JobHistoryHandler(jobPersistence, WorkerEnvironment.DOCKER, LogConfigs.EMPTY, connectionsHandler, sourceHandler, sourceDefinitionsHandler, destinationHandler, destinationDefinitionsHandler, airbyteVersion); } @@ -182,6 +197,9 @@ void testListJobs() throws IOException { when(jobPersistence.listJobs(Set.of(Enums.convertTo(CONFIG_TYPE_FOR_API, ConfigType.class)), JOB_CONFIG_ID, pagesize, rowOffset)) .thenReturn(List.of(latestJobNoAttempt, successfulJob)); when(jobPersistence.getJobCount(Set.of(Enums.convertTo(CONFIG_TYPE_FOR_API, ConfigType.class)), JOB_CONFIG_ID)).thenReturn(2L); + when(jobPersistence.getAttemptStats(List.of(200L, 100L))).thenReturn(Map.of( + new JobAttemptPair(100, 0), ATTEMPT_STATS, + new JobAttemptPair(jobId2, 0), ATTEMPT_STATS)); final var requestBody = new JobListRequestBody() .configTypes(Collections.singletonList(CONFIG_TYPE_FOR_API)) @@ -189,8 +207,8 @@ void testListJobs() throws IOException { .pagination(new Pagination().pageSize(pagesize).rowOffset(rowOffset)); final var jobReadList = jobHistoryHandler.listJobsFor(requestBody); - final var successfulJobWithAttemptRead = new JobWithAttemptsRead().job(toJobInfo(successfulJob)).attempts(ImmutableList.of(toAttemptRead( - testJobAttempt))); + final var expAttemptRead = toAttemptRead(testJobAttempt).totalStats(ATTEMPT_STATS_API).streamStats(ATTEMPT_STREAM_STATS); + final var successfulJobWithAttemptRead = new JobWithAttemptsRead().job(toJobInfo(successfulJob)).attempts(ImmutableList.of(expAttemptRead)); final var latestJobWithAttemptRead = new JobWithAttemptsRead().job(toJobInfo(latestJobNoAttempt)).attempts(Collections.emptyList()); final JobReadList expectedJobReadList = new JobReadList().jobs(List.of(latestJobWithAttemptRead, successfulJobWithAttemptRead)).totalJobCount(2L); @@ -223,6 +241,10 @@ void testListJobsFor() throws IOException { when(jobPersistence.listJobs(configTypes, JOB_CONFIG_ID, pagesize, rowOffset)).thenReturn(List.of(latestJob, secondJob, firstJob)); when(jobPersistence.getJobCount(configTypes, JOB_CONFIG_ID)).thenReturn(3L); + when(jobPersistence.getAttemptStats(List.of(300L, 200L, 100L))).thenReturn(Map.of( + new JobAttemptPair(100, 0), ATTEMPT_STATS, + new JobAttemptPair(secondJobId, 0), ATTEMPT_STATS, + new JobAttemptPair(latestJobId, 0), ATTEMPT_STATS)); final JobListRequestBody requestBody = new JobListRequestBody() .configTypes(List.of(CONFIG_TYPE_FOR_API, JobConfigType.SYNC, JobConfigType.DISCOVER_SCHEMA)) @@ -231,9 +253,11 @@ void testListJobsFor() throws IOException { final JobReadList jobReadList = jobHistoryHandler.listJobsFor(requestBody); final var firstJobWithAttemptRead = - new JobWithAttemptsRead().job(toJobInfo(firstJob)).attempts(ImmutableList.of(toAttemptRead(testJobAttempt))); + new JobWithAttemptsRead().job(toJobInfo(firstJob)) + .attempts(ImmutableList.of(toAttemptRead(testJobAttempt).totalStats(ATTEMPT_STATS_API).streamStats(ATTEMPT_STREAM_STATS))); final var secondJobWithAttemptRead = - new JobWithAttemptsRead().job(toJobInfo(secondJob)).attempts(ImmutableList.of(toAttemptRead(secondJobAttempt))); + new JobWithAttemptsRead().job(toJobInfo(secondJob)) + .attempts(ImmutableList.of(toAttemptRead(secondJobAttempt).totalStats(ATTEMPT_STATS_API).streamStats(ATTEMPT_STREAM_STATS))); final var latestJobWithAttemptRead = new JobWithAttemptsRead().job(toJobInfo(latestJob)).attempts(Collections.emptyList()); final JobReadList expectedJobReadList = new JobReadList().jobs(List.of(latestJobWithAttemptRead, secondJobWithAttemptRead, firstJobWithAttemptRead)).totalJobCount(3L); @@ -257,6 +281,9 @@ void testListJobsIncludingJobId() throws IOException { when(jobPersistence.listJobsIncludingId(Set.of(Enums.convertTo(CONFIG_TYPE_FOR_API, ConfigType.class)), JOB_CONFIG_ID, jobId2, pagesize)) .thenReturn(List.of(latestJobNoAttempt, successfulJob)); when(jobPersistence.getJobCount(Set.of(Enums.convertTo(CONFIG_TYPE_FOR_API, ConfigType.class)), JOB_CONFIG_ID)).thenReturn(2L); + when(jobPersistence.getAttemptStats(List.of(200L, 100L))).thenReturn(Map.of( + new JobAttemptPair(100, 0), ATTEMPT_STATS, + new JobAttemptPair(jobId2, 0), ATTEMPT_STATS)); final var requestBody = new JobListRequestBody() .configTypes(Collections.singletonList(CONFIG_TYPE_FOR_API)) @@ -266,7 +293,7 @@ void testListJobsIncludingJobId() throws IOException { final var jobReadList = jobHistoryHandler.listJobsFor(requestBody); final var successfulJobWithAttemptRead = new JobWithAttemptsRead().job(toJobInfo(successfulJob)).attempts(ImmutableList.of(toAttemptRead( - testJobAttempt))); + testJobAttempt).totalStats(ATTEMPT_STATS_API).streamStats(ATTEMPT_STREAM_STATS))); final var latestJobWithAttemptRead = new JobWithAttemptsRead().job(toJobInfo(latestJobNoAttempt)).attempts(Collections.emptyList()); final JobReadList expectedJobReadList = new JobReadList().jobs(List.of(latestJobWithAttemptRead, successfulJobWithAttemptRead)).totalJobCount(2L); @@ -305,16 +332,16 @@ void testGetJobInfoLight() throws IOException { @Test @DisplayName("Should return the right info to debug this job") void testGetDebugJobInfo() throws IOException, JsonValidationException, ConfigNotFoundException, URISyntaxException { - standardSourceDefinition = SourceDefinitionHelpers.generateSourceDefinition(); + final StandardSourceDefinition standardSourceDefinition = SourceDefinitionHelpers.generateSourceDefinition(); final SourceConnection source = SourceHelpers.generateSource(UUID.randomUUID()); - sourceRead = SourceHelpers.getSourceRead(source, standardSourceDefinition); + final SourceRead sourceRead = SourceHelpers.getSourceRead(source, standardSourceDefinition); - standardDestinationDefinition = DestinationDefinitionHelpers.generateDestination(); + final StandardDestinationDefinition standardDestinationDefinition = DestinationDefinitionHelpers.generateDestination(); final DestinationConnection destination = DestinationHelpers.generateDestination(UUID.randomUUID()); - destinationRead = DestinationHelpers.getDestinationRead(destination, standardDestinationDefinition); + final DestinationRead destinationRead = DestinationHelpers.getDestinationRead(destination, standardDestinationDefinition); final StandardSync standardSync = ConnectionHelpers.generateSyncWithSourceId(source.getSourceId()); - connectionRead = ConnectionHelpers.generateExpectedConnectionRead(standardSync); + final ConnectionRead connectionRead = ConnectionHelpers.generateExpectedConnectionRead(standardSync); when(connectionsHandler.getConnection(UUID.fromString(testJob.getScope()))).thenReturn(connectionRead); final SourceIdRequestBody sourceIdRequestBody = new SourceIdRequestBody(); @@ -325,10 +352,13 @@ void testGetDebugJobInfo() throws IOException, JsonValidationException, ConfigNo destinationIdRequestBody.setDestinationId(connectionRead.getDestinationId()); when(destinationHandler.getDestination(destinationIdRequestBody)).thenReturn(destinationRead); when(jobPersistence.getJob(JOB_ID)).thenReturn(testJob); + when(jobPersistence.getAttemptStats(anyLong(), anyInt())).thenReturn(ATTEMPT_STATS); final JobIdRequestBody requestBody = new JobIdRequestBody().id(JOB_ID); final JobDebugInfoRead jobDebugInfoActual = jobHistoryHandler.getJobDebugInfo(requestBody); - final JobDebugInfoRead exp = new JobDebugInfoRead().job(toDebugJobInfo(testJob)).attempts(toAttemptInfoList(ImmutableList.of(testJobAttempt))); + final List attemptInfoReads = toAttemptInfoList(ImmutableList.of(testJobAttempt)); + attemptInfoReads.forEach(read -> read.getAttempt().totalStats(ATTEMPT_STATS_API).streamStats(ATTEMPT_STREAM_STATS)); + final JobDebugInfoRead exp = new JobDebugInfoRead().job(toDebugJobInfo(testJob)).attempts(attemptInfoReads); assertEquals(exp, jobDebugInfoActual); } diff --git a/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/activities/JobCreationAndStatusUpdateActivityImpl.java b/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/activities/JobCreationAndStatusUpdateActivityImpl.java index 39b089f8fef2..37a9f7647629 100644 --- a/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/activities/JobCreationAndStatusUpdateActivityImpl.java +++ b/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/activities/JobCreationAndStatusUpdateActivityImpl.java @@ -431,7 +431,7 @@ private boolean checkActiveJobPreviousAttempt(final Job activeJob, final int att if (activeJob.getAttempts().size() > minAttemptSize) { final Optional optionalAttempt = activeJob.getAttempts().stream() - .filter(attempt -> attempt.getId() == (attemptId - 1)).findFirst(); + .filter(attempt -> attempt.getAttemptNumber() == (attemptId - 1)).findFirst(); result = optionalAttempt.isPresent() && optionalAttempt.get().getStatus().equals(FAILED); } @@ -451,8 +451,7 @@ private void failNonTerminalJobs(final UUID connectionId) { continue; } - // the Attempt object 'id' is actually the value of the attempt_number column in the db - final int attemptNumber = (int) attempt.getId(); + final int attemptNumber = attempt.getAttemptNumber(); log.info("Failing non-terminal attempt {} for non-terminal job {}", attemptNumber, jobId); jobPersistence.failAttempt(jobId, attemptNumber); jobPersistence.writeAttemptFailureSummary(jobId, attemptNumber, From 88c3518ee3d7d4cc779db188f6bd940806644998 Mon Sep 17 00:00:00 2001 From: Benoit Moriceau Date: Thu, 5 Jan 2023 16:38:39 -0800 Subject: [PATCH 010/170] Rm temporal version (#21045) * Rm temporal version * Remove temporal version * Update the replayed workflow * Format * Fix pmd --- .../ConnectionManagerWorkflowImpl.java | 220 +-- .../connectionManagerWorkflowHistory.json | 1722 ++++++++++------- 2 files changed, 1070 insertions(+), 872 deletions(-) diff --git a/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/ConnectionManagerWorkflowImpl.java b/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/ConnectionManagerWorkflowImpl.java index d05db6d4fa73..36481b65eee1 100644 --- a/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/ConnectionManagerWorkflowImpl.java +++ b/airbyte-workers/src/main/java/io/airbyte/workers/temporal/scheduling/ConnectionManagerWorkflowImpl.java @@ -11,7 +11,6 @@ import com.fasterxml.jackson.databind.JsonNode; import datadog.trace.api.Trace; -import io.airbyte.commons.temporal.TemporalJobType; import io.airbyte.commons.temporal.TemporalWorkflowUtils; import io.airbyte.commons.temporal.exception.RetryableException; import io.airbyte.commons.temporal.scheduling.ConnectionManagerWorkflow; @@ -21,12 +20,10 @@ import io.airbyte.commons.temporal.scheduling.state.WorkflowState; import io.airbyte.commons.temporal.scheduling.state.listener.NoopStateListener; import io.airbyte.config.ConnectorJobOutput; -import io.airbyte.config.ConnectorJobOutput.OutputType; import io.airbyte.config.FailureReason; import io.airbyte.config.FailureReason.FailureType; import io.airbyte.config.NormalizationSummary; import io.airbyte.config.StandardCheckConnectionInput; -import io.airbyte.config.StandardCheckConnectionOutput; import io.airbyte.config.StandardSyncInput; import io.airbyte.config.StandardSyncOutput; import io.airbyte.config.StandardSyncSummary; @@ -48,22 +45,17 @@ import io.airbyte.workers.temporal.scheduling.activities.ConfigFetchActivity.ScheduleRetrieverOutput; import io.airbyte.workers.temporal.scheduling.activities.GenerateInputActivity; import io.airbyte.workers.temporal.scheduling.activities.GenerateInputActivity.GeneratedJobInput; -import io.airbyte.workers.temporal.scheduling.activities.GenerateInputActivity.SyncInput; import io.airbyte.workers.temporal.scheduling.activities.GenerateInputActivity.SyncInputWithAttemptNumber; import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity; import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.AttemptCreationInput; -import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.AttemptCreationOutput; -import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.AttemptFailureInput; import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.AttemptNumberCreationOutput; import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.AttemptNumberFailureInput; import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.EnsureCleanJobStateInput; -import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.JobCancelledInput; import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.JobCancelledInputWithAttemptNumber; import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.JobCheckFailureInput; import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.JobCreationInput; import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.JobCreationOutput; import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.JobFailureInput; -import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.JobSuccessInput; import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.JobSuccessInputWithAttemptNumber; import io.airbyte.workers.temporal.scheduling.activities.JobCreationAndStatusUpdateActivity.ReportJobStartInput; import io.airbyte.workers.temporal.scheduling.activities.RecordMetricActivity; @@ -97,33 +89,6 @@ @SuppressWarnings("PMD.AvoidDuplicateLiterals") public class ConnectionManagerWorkflowImpl implements ConnectionManagerWorkflow { - private static final int TASK_QUEUE_CHANGE_CURRENT_VERSION = 1; - private static final int AUTO_DISABLE_FAILING_CONNECTION_CHANGE_CURRENT_VERSION = 1; - - private static final String RENAME_ATTEMPT_ID_TO_NUMBER_TAG = "rename_attempt_id_to_number"; - private static final int RENAME_ATTEMPT_ID_TO_NUMBER_CURRENT_VERSION = 1; - - private static final String CHECK_PREVIOUS_JOB_OR_ATTEMPT_TAG = "check_previous_job_or_attempt"; - private static final int CHECK_PREVIOUS_JOB_OR_ATTEMPT_TAG_CURRENT_VERSION = 1; - - private static final String ENSURE_CLEAN_JOB_STATE = "ensure_clean_job_state"; - private static final int ENSURE_CLEAN_JOB_STATE_CURRENT_VERSION = 1; - - private static final String CHECK_BEFORE_SYNC_TAG = "check_before_sync"; - private static final int CHECK_BEFORE_SYNC_CURRENT_VERSION = 1; - - private static final String CHECK_JOB_OUTPUT_TAG = "check_job_output"; - private static final int CHECK_JOB_OUTPUT_TAG_CURRENT_VERSION = 1; - - private static final String DELETE_RESET_JOB_STREAMS_TAG = "delete_reset_job_streams"; - private static final int DELETE_RESET_JOB_STREAMS_CURRENT_VERSION = 1; - private static final String RECORD_METRIC_TAG = "record_metric"; - private static final int RECORD_METRIC_CURRENT_VERSION = 1; - private static final String WORKFLOW_CONFIG_TAG = "workflow_config"; - private static final int WORKFLOW_CONFIG_CURRENT_VERSION = 1; - private static final String ROUTE_ACTIVITY_TAG = "route_activity"; - private static final int ROUTE_ACTIVITY_CURRENT_VERSION = 1; - private WorkflowState workflowState = new WorkflowState(UUID.randomUUID(), new NoopStateListener()); private final WorkflowInternalState workflowInternalState = new WorkflowInternalState(); @@ -299,22 +264,12 @@ private CancellationScope generateSyncWorkflowRunnable(final ConnectionUpdaterIn private void reportSuccess(final ConnectionUpdaterInput connectionUpdaterInput, final StandardSyncOutput standardSyncOutput) { workflowState.setSuccess(true); - final int attemptCreationVersion = - Workflow.getVersion(RENAME_ATTEMPT_ID_TO_NUMBER_TAG, Workflow.DEFAULT_VERSION, RENAME_ATTEMPT_ID_TO_NUMBER_CURRENT_VERSION); - - if (attemptCreationVersion < RENAME_ATTEMPT_ID_TO_NUMBER_CURRENT_VERSION) { - runMandatoryActivity(jobCreationAndStatusUpdateActivity::jobSuccess, new JobSuccessInput( - workflowInternalState.getJobId(), - workflowInternalState.getAttemptNumber(), - connectionUpdaterInput.getConnectionId(), - standardSyncOutput)); - } else { - runMandatoryActivity(jobCreationAndStatusUpdateActivity::jobSuccessWithAttemptNumber, new JobSuccessInputWithAttemptNumber( - workflowInternalState.getJobId(), - workflowInternalState.getAttemptNumber(), - connectionUpdaterInput.getConnectionId(), - standardSyncOutput)); - } + + runMandatoryActivity(jobCreationAndStatusUpdateActivity::jobSuccessWithAttemptNumber, new JobSuccessInputWithAttemptNumber( + workflowInternalState.getJobId(), + workflowInternalState.getAttemptNumber(), + connectionUpdaterInput.getConnectionId(), + standardSyncOutput)); deleteResetJobStreams(); @@ -334,25 +289,15 @@ private void reportFailure(final ConnectionUpdaterInput connectionUpdaterInput, final StandardSyncOutput standardSyncOutput, final FailureCause failureCause, final Set failureReasonsOverride) { - final int attemptCreationVersion = - Workflow.getVersion(RENAME_ATTEMPT_ID_TO_NUMBER_TAG, Workflow.DEFAULT_VERSION, RENAME_ATTEMPT_ID_TO_NUMBER_CURRENT_VERSION); final Set failureReasons = failureReasonsOverride.isEmpty() ? workflowInternalState.getFailures() : failureReasonsOverride; - if (attemptCreationVersion < RENAME_ATTEMPT_ID_TO_NUMBER_CURRENT_VERSION) { - runMandatoryActivity(jobCreationAndStatusUpdateActivity::attemptFailure, new AttemptFailureInput( - workflowInternalState.getJobId(), - workflowInternalState.getAttemptNumber(), - connectionUpdaterInput.getConnectionId(), - standardSyncOutput, - FailureHelper.failureSummary(failureReasons, workflowInternalState.getPartialSuccess()))); - } else { - runMandatoryActivity(jobCreationAndStatusUpdateActivity::attemptFailureWithAttemptNumber, new AttemptNumberFailureInput( - workflowInternalState.getJobId(), - workflowInternalState.getAttemptNumber(), - connectionUpdaterInput.getConnectionId(), - standardSyncOutput, - FailureHelper.failureSummary(failureReasons, workflowInternalState.getPartialSuccess()))); - } + + runMandatoryActivity(jobCreationAndStatusUpdateActivity::attemptFailureWithAttemptNumber, new AttemptNumberFailureInput( + workflowInternalState.getJobId(), + workflowInternalState.getAttemptNumber(), + connectionUpdaterInput.getConnectionId(), + standardSyncOutput, + FailureHelper.failureSummary(failureReasons, workflowInternalState.getPartialSuccess()))); final int maxAttempt = configFetchActivity.getMaxAttempt().getMaxAttempt(); final int attemptNumber = connectionUpdaterInput.getAttemptNumber(); @@ -371,17 +316,12 @@ private void reportFailure(final ConnectionUpdaterInput connectionUpdaterInput, runMandatoryActivity(jobCreationAndStatusUpdateActivity::jobFailure, new JobFailureInput(connectionUpdaterInput.getJobId(), connectionUpdaterInput.getAttemptNumber(), connectionUpdaterInput.getConnectionId(), failureReason)); - final int autoDisableConnectionVersion = - Workflow.getVersion("auto_disable_failing_connection", Workflow.DEFAULT_VERSION, AUTO_DISABLE_FAILING_CONNECTION_CHANGE_CURRENT_VERSION); - - if (autoDisableConnectionVersion != Workflow.DEFAULT_VERSION) { - final AutoDisableConnectionActivityInput autoDisableConnectionActivityInput = - new AutoDisableConnectionActivityInput(connectionId, Instant.ofEpochMilli(Workflow.currentTimeMillis())); - final AutoDisableConnectionOutput output = runMandatoryActivityWithOutput( - autoDisableConnectionActivity::autoDisableFailingConnection, autoDisableConnectionActivityInput); - if (output.isDisabled()) { - log.info("Auto-disabled for constantly failing for Connection {}", connectionId); - } + final AutoDisableConnectionActivityInput autoDisableConnectionActivityInput = + new AutoDisableConnectionActivityInput(connectionId, Instant.ofEpochMilli(Workflow.currentTimeMillis())); + final AutoDisableConnectionOutput output = runMandatoryActivityWithOutput( + autoDisableConnectionActivity::autoDisableFailingConnection, autoDisableConnectionActivityInput); + if (output.isDisabled()) { + log.info("Auto-disabled for constantly failing for Connection {}", connectionId); } // Record the failure metric @@ -392,14 +332,6 @@ private void reportFailure(final ConnectionUpdaterInput connectionUpdaterInput, } private ConnectorJobOutput getCheckResponse(final CheckConnectionInput checkInput) { - final int checkJobOutputVersion = - Workflow.getVersion(CHECK_JOB_OUTPUT_TAG, Workflow.DEFAULT_VERSION, CHECK_JOB_OUTPUT_TAG_CURRENT_VERSION); - - if (checkJobOutputVersion < CHECK_JOB_OUTPUT_TAG_CURRENT_VERSION) { - final StandardCheckConnectionOutput checkOutput = runMandatoryActivityWithOutput(checkActivity::run, checkInput); - return new ConnectorJobOutput().withOutputType(OutputType.CHECK_CONNECTION).withCheckConnection(checkOutput); - } - return runMandatoryActivityWithOutput(checkActivity::runWithJobOutput, checkInput); } @@ -412,26 +344,13 @@ private SyncCheckConnectionFailure checkConnections(final GenerateInputActivity. final IntegrationLauncherConfig destinationLauncherConfig = jobInputs.getDestinationLauncherConfig(); final SyncCheckConnectionFailure checkFailure = new SyncCheckConnectionFailure(jobRunConfig); - final int attemptCreationVersion = - Workflow.getVersion(CHECK_BEFORE_SYNC_TAG, Workflow.DEFAULT_VERSION, CHECK_BEFORE_SYNC_CURRENT_VERSION); - - if (attemptCreationVersion < CHECK_BEFORE_SYNC_CURRENT_VERSION) { - // return early if this instance of the workflow was created beforehand - return checkFailure; - } - final StandardCheckConnectionInput sourceConfiguration = new StandardCheckConnectionInput().withConnectionConfiguration(sourceConfig); final CheckConnectionInput checkSourceInput = new CheckConnectionInput(jobRunConfig, sourceLauncherConfig, sourceConfiguration); - final int checkJobOutputVersion = - Workflow.getVersion(CHECK_PREVIOUS_JOB_OR_ATTEMPT_TAG, Workflow.DEFAULT_VERSION, CHECK_PREVIOUS_JOB_OR_ATTEMPT_TAG_CURRENT_VERSION); - boolean isLastJobOrAttemptFailure = true; - - if (checkJobOutputVersion >= CHECK_PREVIOUS_JOB_OR_ATTEMPT_TAG_CURRENT_VERSION) { - final JobCheckFailureInput jobStateInput = - new JobCheckFailureInput(Long.parseLong(jobRunConfig.getJobId()), jobRunConfig.getAttemptId().intValue(), connectionId); - isLastJobOrAttemptFailure = runMandatoryActivityWithOutput(jobCreationAndStatusUpdateActivity::isLastJobOrAttemptFailure, jobStateInput); - } + final JobCheckFailureInput jobStateInput = + new JobCheckFailureInput(Long.parseLong(jobRunConfig.getJobId()), jobRunConfig.getAttemptId().intValue(), connectionId); + final boolean isLastJobOrAttemptFailure = + runMandatoryActivityWithOutput(jobCreationAndStatusUpdateActivity::isLastJobOrAttemptFailure, jobStateInput); if (isResetJob(sourceLauncherConfig) || checkFailure.isFailed() || !isLastJobOrAttemptFailure) { // reset jobs don't need to connect to any external source, so check connection is unnecessary log.info("SOURCE CHECK: Skipped"); @@ -693,14 +612,6 @@ private Duration getTimeToWait(final UUID connectionId) { } private void ensureCleanJobState(final ConnectionUpdaterInput connectionUpdaterInput) { - final int ensureCleanJobStateVersion = - Workflow.getVersion(ENSURE_CLEAN_JOB_STATE, Workflow.DEFAULT_VERSION, ENSURE_CLEAN_JOB_STATE_CURRENT_VERSION); - - // For backwards compatibility and determinism, skip if workflow existed before this change - if (ensureCleanJobStateVersion < ENSURE_CLEAN_JOB_STATE_CURRENT_VERSION) { - return; - } - if (connectionUpdaterInput.getJobId() != null) { log.info("This workflow is already attached to a job, so no need to clean job state."); return; @@ -710,13 +621,6 @@ private void ensureCleanJobState(final ConnectionUpdaterInput connectionUpdaterI } private void recordMetric(final RecordMetricInput recordMetricInput) { - final int recordMetricVersion = - Workflow.getVersion(RECORD_METRIC_TAG, Workflow.DEFAULT_VERSION, RECORD_METRIC_CURRENT_VERSION); - - if (recordMetricVersion < RECORD_METRIC_CURRENT_VERSION) { - return; - } - runMandatoryActivity(recordMetricActivity::recordWorkflowCountMetric, recordMetricInput); } @@ -747,19 +651,6 @@ private Long getOrCreateJobId(final ConnectionUpdaterInput connectionUpdaterInpu * @return The attempt number */ private Integer createAttempt(final long jobId) { - final int attemptCreationVersion = - Workflow.getVersion(RENAME_ATTEMPT_ID_TO_NUMBER_TAG, Workflow.DEFAULT_VERSION, RENAME_ATTEMPT_ID_TO_NUMBER_CURRENT_VERSION); - - // Retrieve the attempt number but name it attempt id - if (attemptCreationVersion < RENAME_ATTEMPT_ID_TO_NUMBER_CURRENT_VERSION) { - final AttemptCreationOutput attemptCreationOutput = - runMandatoryActivityWithOutput( - jobCreationAndStatusUpdateActivity::createNewAttempt, - new AttemptCreationInput( - jobId)); - return attemptCreationOutput.getAttemptId(); - } - final AttemptNumberCreationOutput attemptNumberCreationOutput = runMandatoryActivityWithOutput( jobCreationAndStatusUpdateActivity::createNewAttemptNumber, @@ -775,20 +666,6 @@ private Integer createAttempt(final long jobId) { private GeneratedJobInput getJobInput() { final Long jobId = workflowInternalState.getJobId(); final Integer attemptNumber = workflowInternalState.getAttemptNumber(); - final int attemptCreationVersion = - Workflow.getVersion(RENAME_ATTEMPT_ID_TO_NUMBER_TAG, Workflow.DEFAULT_VERSION, RENAME_ATTEMPT_ID_TO_NUMBER_CURRENT_VERSION); - - if (attemptCreationVersion < RENAME_ATTEMPT_ID_TO_NUMBER_CURRENT_VERSION) { - final SyncInput getSyncInputActivitySyncInput = new SyncInput( - attemptNumber, - jobId); - - final GeneratedJobInput syncWorkflowInputs = runMandatoryActivityWithOutput( - getSyncInputActivity::getSyncWorkflowInput, - getSyncInputActivitySyncInput); - - return syncWorkflowInputs; - } final SyncInputWithAttemptNumber getSyncInputActivitySyncInput = new SyncInputWithAttemptNumber( attemptNumber, @@ -802,18 +679,6 @@ private GeneratedJobInput getJobInput() { } private String getSyncTaskQueue() { - final int taskQueueChangeVersion = - Workflow.getVersion("task_queue_change_from_connection_updater_to_sync", Workflow.DEFAULT_VERSION, TASK_QUEUE_CHANGE_CURRENT_VERSION); - - if (taskQueueChangeVersion < TASK_QUEUE_CHANGE_CURRENT_VERSION) { - return TemporalJobType.CONNECTION_UPDATER.name(); - } - - final int routeActivityVersion = Workflow.getVersion(ROUTE_ACTIVITY_TAG, Workflow.DEFAULT_VERSION, ROUTE_ACTIVITY_CURRENT_VERSION); - - if (routeActivityVersion < ROUTE_ACTIVITY_CURRENT_VERSION) { - return TemporalJobType.SYNC.name(); - } final RouteToSyncTaskQueueInput routeToSyncTaskQueueInput = new RouteToSyncTaskQueueInput(connectionId); final RouteToSyncTaskQueueOutput routeToSyncTaskQueueOutput = runMandatoryActivityWithOutput( @@ -910,46 +775,21 @@ private void reportCancelled(final UUID connectionId) { final Integer attemptNumber = workflowInternalState.getAttemptNumber(); final Set failures = workflowInternalState.getFailures(); final Boolean partialSuccess = workflowInternalState.getPartialSuccess(); - final int attemptCreationVersion = - Workflow.getVersion(RENAME_ATTEMPT_ID_TO_NUMBER_TAG, Workflow.DEFAULT_VERSION, RENAME_ATTEMPT_ID_TO_NUMBER_CURRENT_VERSION); - - if (attemptCreationVersion < RENAME_ATTEMPT_ID_TO_NUMBER_CURRENT_VERSION) { - runMandatoryActivity(jobCreationAndStatusUpdateActivity::jobCancelled, - new JobCancelledInput( - jobId, - attemptNumber, - connectionId, - FailureHelper.failureSummaryForCancellation(jobId, attemptNumber, failures, partialSuccess))); - } else { - runMandatoryActivity(jobCreationAndStatusUpdateActivity::jobCancelledWithAttemptNumber, - new JobCancelledInputWithAttemptNumber( - jobId, - attemptNumber, - connectionId, - FailureHelper.failureSummaryForCancellation(jobId, attemptNumber, failures, partialSuccess))); - } + + runMandatoryActivity(jobCreationAndStatusUpdateActivity::jobCancelledWithAttemptNumber, + new JobCancelledInputWithAttemptNumber( + jobId, + attemptNumber, + connectionId, + FailureHelper.failureSummaryForCancellation(jobId, attemptNumber, failures, partialSuccess))); } private void deleteResetJobStreams() { - final int deleteResetJobStreamsVersion = - Workflow.getVersion(DELETE_RESET_JOB_STREAMS_TAG, Workflow.DEFAULT_VERSION, DELETE_RESET_JOB_STREAMS_CURRENT_VERSION); - - if (deleteResetJobStreamsVersion < DELETE_RESET_JOB_STREAMS_CURRENT_VERSION) { - return; - } - runMandatoryActivity(streamResetActivity::deleteStreamResetRecordsForJob, new DeleteStreamResetRecordsForJobInput(connectionId, workflowInternalState.getJobId())); } private Duration getWorkflowRestartDelaySeconds() { - final int workflowConfigVersion = - Workflow.getVersion(WORKFLOW_CONFIG_TAG, Workflow.DEFAULT_VERSION, WORKFLOW_CONFIG_CURRENT_VERSION); - - if (workflowConfigVersion < WORKFLOW_CONFIG_CURRENT_VERSION) { - return Duration.ofMinutes(10L); - } - return workflowConfigActivity.getWorkflowRestartDelaySeconds(); } diff --git a/airbyte-workers/src/test/resources/connectionManagerWorkflowHistory.json b/airbyte-workers/src/test/resources/connectionManagerWorkflowHistory.json index 877ab2f10500..5436ff33d747 100644 --- a/airbyte-workers/src/test/resources/connectionManagerWorkflowHistory.json +++ b/airbyte-workers/src/test/resources/connectionManagerWorkflowHistory.json @@ -2,16 +2,15 @@ "events": [ { "eventId": "1", - "eventTime": "2022-07-19T15:29:54.769499236Z", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "WorkflowExecutionStarted", - "version": "1011", - "taskId": "374953777", + "version": "0", + "taskId": "5242885", "workflowExecutionStartedEventAttributes": { "workflowType": { "name": "ConnectionManagerWorkflow" }, "parentWorkflowNamespace": "", - "parentWorkflowExecution": null, "parentInitiatedEventId": "0", "taskQueue": { "name": "CONNECTION_UPDATER", @@ -23,30 +22,28 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJjb25uZWN0aW9uSWQiOiJkZjdlNjg4Ni00NTgxLTQwZWUtYmFlNS05ZTdmZjI3MWRhM2MiLCJqb2JJZCI6bnVsbCwiYXR0ZW1wdElkIjpudWxsLCJmcm9tRmFpbHVyZSI6ZmFsc2UsImF0dGVtcHROdW1iZXIiOjEsIndvcmtmbG93U3RhdGUiOm51bGwsInJlc2V0Q29ubmVjdGlvbiI6ZmFsc2UsImZyb21Kb2JSZXNldEZhaWx1cmUiOmZhbHNlLCJza2lwU2NoZWR1bGluZyI6ZmFsc2V9" + "data": "eyJjb25uZWN0aW9uSWQiOiJiNjFkNWFjZC1iZjczLTRlZTctOGQ0Ni02OGY0MmQ5NTNmMjgiLCJqb2JJZCI6bnVsbCwiYXR0ZW1wdElkIjpudWxsLCJmcm9tRmFpbHVyZSI6ZmFsc2UsImF0dGVtcHROdW1iZXIiOjEsIndvcmtmbG93U3RhdGUiOm51bGwsInJlc2V0Q29ubmVjdGlvbiI6ZmFsc2UsImZyb21Kb2JSZXNldEZhaWx1cmUiOmZhbHNlLCJza2lwU2NoZWR1bGluZyI6ZmFsc2V9" } ] }, "workflowExecutionTimeout": "0s", "workflowRunTimeout": "0s", "workflowTaskTimeout": "10s", - "continuedExecutionRunId": "c3d64ae4-3ab4-49a9-801e-e6d0a44c4cbd", - "initiator": "Workflow", - "continuedFailure": null, - "lastCompletionResult": null, - "originalExecutionRunId": "adbe51a0-6d72-4e51-afed-98f7df878e4c", - "identity": "", - "firstExecutionRunId": "82a509a0-33fd-4f02-a9f4-24d1a5277d0e", - "retryPolicy": null, + "continuedExecutionRunId": "", + "initiator": "Unspecified", + "originalExecutionRunId": "ded7dcb2-f516-4d19-ad23-044718e64083", + "identity": "1@e0ef4adc0b34", + "firstExecutionRunId": "ded7dcb2-f516-4d19-ad23-044718e64083", + "retryPolicy": { + "nonRetryableErrorTypes": [], + "initialInterval": "1s", + "backoffCoefficient": 2, + "maximumInterval": "100s", + "maximumAttempts": 1 + }, "attempt": 1, - "workflowExecutionExpirationTime": null, "cronSchedule": "", - "firstWorkflowTaskBackoff": null, - "memo": null, - "searchAttributes": null, - "prevAutoResetPoints": { - "points": [] - }, + "firstWorkflowTaskBackoff": "0s", "header": { "fields": {} } @@ -54,10 +51,10 @@ }, { "eventId": "2", - "eventTime": "2022-07-19T15:29:54.769536056Z", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "374953778", + "version": "0", + "taskId": "5242886", "workflowTaskScheduledEventAttributes": { "taskQueue": { "name": "CONNECTION_UPDATER", @@ -69,37 +66,36 @@ }, { "eventId": "3", - "eventTime": "2022-07-19T15:29:54.790828057Z", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "374953785", + "version": "0", + "taskId": "5242890", "workflowTaskStartedEventAttributes": { "scheduledEventId": "2", - "identity": "1@airbyte-worker-74797b7bc-rm4tt", - "requestId": "90699e6b-5c36-48e5-b7d2-806a044ccbb5" + "identity": "1@0a5c2d703445", + "requestId": "67367063-1e90-4580-a832-13d3a52ab614" } }, { "eventId": "4", - "eventTime": "2022-07-19T15:29:54.850282807Z", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "374953788", + "version": "0", + "taskId": "5242893", "workflowTaskCompletedEventAttributes": { "scheduledEventId": "2", "startedEventId": "3", - "identity": "1@airbyte-worker-74797b7bc-rm4tt", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { "eventId": "5", - "eventTime": "2022-07-19T15:29:54.850308217Z", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "MarkerRecorded", - "version": "1011", - "taskId": "374953789", + "version": "0", + "taskId": "5242894", "markerRecordedEventAttributes": { - "markerName": "Version", "details": { "changeId": { "payloads": [ @@ -107,7 +103,7 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "ImVuc3VyZV9jbGVhbl9qb2Jfc3RhdGUi" + "data": "IndvcmtmbG93X2NvbmZpZyI=" } ] }, @@ -122,21 +118,20 @@ ] } }, - "workflowTaskCompletedEventId": "4", - "header": null, - "failure": null + "markerName": "Version", + "workflowTaskCompletedEventId": "4" } }, { "eventId": "6", - "eventTime": "2022-07-19T15:29:54.850319877Z", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "ActivityTaskScheduled", - "version": "1011", - "taskId": "374953790", + "version": "0", + "taskId": "5242895", "activityTaskScheduledEventAttributes": { - "activityId": "597b5021-166b-3cfa-a31b-1bcd249758a3", + "activityId": "1c205445-80b5-38b1-b91d-8982c6db7e12", "activityType": { - "name": "EnsureCleanJobState" + "name": "GetWorkflowRestartDelaySeconds" }, "namespace": "", "taskQueue": { @@ -146,66 +141,64 @@ "header": { "fields": {} }, - "input": { - "payloads": [ - { - "metadata": { - "encoding": "anNvbi9wbGFpbg==" - }, - "data": "eyJjb25uZWN0aW9uSWQiOiJkZjdlNjg4Ni00NTgxLTQwZWUtYmFlNS05ZTdmZjI3MWRhM2MifQ==" - } - ] - }, "scheduleToCloseTimeout": "0s", "scheduleToStartTimeout": "0s", "startToCloseTimeout": "120s", "heartbeatTimeout": "30s", "workflowTaskCompletedEventId": "4", "retryPolicy": { + "nonRetryableErrorTypes": [], "initialInterval": "30s", "backoffCoefficient": 2, "maximumInterval": "600s", - "maximumAttempts": 5, - "nonRetryableErrorTypes": [] + "maximumAttempts": 5 } } }, { "eventId": "7", - "eventTime": "2022-07-19T15:29:54.865775178Z", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "ActivityTaskStarted", - "version": "1011", - "taskId": "374953794", + "version": "0", + "taskId": "5242899", "activityTaskStartedEventAttributes": { "scheduledEventId": "6", - "identity": "1@airbyte-worker-74797b7bc-z8dzz", - "requestId": "4ff9e2f4-56d2-4061-8b52-52818870c32d", - "attempt": 1, - "lastFailure": null + "identity": "1@0a5c2d703445", + "requestId": "946c2ad1-6114-4234-ae32-a14c6a1138fe", + "attempt": 1 } }, { "eventId": "8", - "eventTime": "2022-07-19T15:29:54.927630936Z", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "ActivityTaskCompleted", - "version": "1011", - "taskId": "374953795", + "version": "0", + "taskId": "5242900", "activityTaskCompletedEventAttributes": { - "result": null, + "result": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "NjAwLjAwMDAwMDAwMA==" + } + ] + }, "scheduledEventId": "6", "startedEventId": "7", - "identity": "1@airbyte-worker-74797b7bc-z8dzz" + "identity": "1@0a5c2d703445" } }, { "eventId": "9", - "eventTime": "2022-07-19T15:29:54.927641216Z", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "374953796", + "version": "0", + "taskId": "5242901", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-rm4tt:a529a431-c1ae-433e-911e-8e8b9fb3c930", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -214,39 +207,72 @@ }, { "eventId": "10", - "eventTime": "2022-07-19T15:29:54.942954197Z", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "374953800", + "version": "0", + "taskId": "5242905", "workflowTaskStartedEventAttributes": { "scheduledEventId": "9", - "identity": "a529a431-c1ae-433e-911e-8e8b9fb3c930", - "requestId": "32a82bc6-a506-48ba-a49b-62192d64a427" + "identity": "1@0a5c2d703445", + "requestId": "c2b47678-50ab-4b31-b1d2-6d68e368c7de" } }, { "eventId": "11", - "eventTime": "2022-07-19T15:29:55.000970771Z", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "374953803", + "version": "0", + "taskId": "5242908", "workflowTaskCompletedEventAttributes": { "scheduledEventId": "9", "startedEventId": "10", - "identity": "1@airbyte-worker-74797b7bc-rm4tt", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { "eventId": "12", - "eventTime": "2022-07-19T15:29:55.000999591Z", + "eventTime": "2023-01-05T22:40:07.000Z", + "eventType": "MarkerRecorded", + "version": "0", + "taskId": "5242909", + "markerRecordedEventAttributes": { + "details": { + "version": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "MQ==" + } + ] + }, + "changeId": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "InJlY29yZF9tZXRyaWMi" + } + ] + } + }, + "markerName": "Version", + "workflowTaskCompletedEventId": "11" + } + }, + { + "eventId": "13", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "ActivityTaskScheduled", - "version": "1011", - "taskId": "374953804", + "version": "0", + "taskId": "5242910", "activityTaskScheduledEventAttributes": { - "activityId": "701fa7e3-269d-3272-8257-f4b16a6a6782", + "activityId": "2d35ac13-e004-37d7-b563-6da6862e55c7", "activityType": { - "name": "GetTimeToWait" + "name": "RecordWorkflowCountMetric" }, "namespace": "", "taskQueue": { @@ -262,7 +288,7 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJjb25uZWN0aW9uSWQiOiJkZjdlNjg4Ni00NTgxLTQwZWUtYmFlNS05ZTdmZjI3MWRhM2MifQ==" + "data": "eyJjb25uZWN0aW9uVXBkYXRlcklucHV0Ijp7ImNvbm5lY3Rpb25JZCI6ImI2MWQ1YWNkLWJmNzMtNGVlNy04ZDQ2LTY4ZjQyZDk1M2YyOCIsImpvYklkIjpudWxsLCJhdHRlbXB0SWQiOm51bGwsImZyb21GYWlsdXJlIjpmYWxzZSwiYXR0ZW1wdE51bWJlciI6MSwid29ya2Zsb3dTdGF0ZSI6bnVsbCwicmVzZXRDb25uZWN0aW9uIjpmYWxzZSwiZnJvbUpvYlJlc2V0RmFpbHVyZSI6ZmFsc2UsInNraXBTY2hlZHVsaW5nIjpmYWxzZX0sImZhaWx1cmVDYXVzZSI6bnVsbCwibWV0cmljTmFtZSI6IlRFTVBPUkFMX1dPUktGTE9XX0FUVEVNUFQiLCJtZXRyaWNBdHRyaWJ1dGVzIjpudWxsfQ==" } ] }, @@ -272,59 +298,189 @@ "heartbeatTimeout": "30s", "workflowTaskCompletedEventId": "11", "retryPolicy": { + "nonRetryableErrorTypes": [], "initialInterval": "30s", "backoffCoefficient": 2, "maximumInterval": "600s", - "maximumAttempts": 5, - "nonRetryableErrorTypes": [] + "maximumAttempts": 5 } } }, { - "eventId": "13", - "eventTime": "2022-07-19T15:29:55.016106672Z", + "eventId": "14", + "eventTime": "2023-01-05T22:40:07.000Z", "eventType": "ActivityTaskStarted", - "version": "1011", - "taskId": "374953808", + "version": "0", + "taskId": "5242914", "activityTaskStartedEventAttributes": { - "scheduledEventId": "12", - "identity": "1@airbyte-worker-74797b7bc-q4jfs", - "requestId": "4a97e48b-c929-4a32-a425-bc911781c9ee", - "attempt": 1, - "lastFailure": null + "scheduledEventId": "13", + "identity": "1@0a5c2d703445", + "requestId": "8c012b95-8326-49ad-9b68-4ac8662fbce1", + "attempt": 1 } }, { - "eventId": "14", - "eventTime": "2022-07-19T15:29:55.079278454Z", + "eventId": "15", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskCompleted", - "version": "1011", - "taskId": "374953809", + "version": "0", + "taskId": "5242915", "activityTaskCompletedEventAttributes": { - "result": { + "scheduledEventId": "13", + "startedEventId": "14", + "identity": "1@0a5c2d703445" + } + }, + { + "eventId": "16", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "WorkflowTaskScheduled", + "version": "0", + "taskId": "5242916", + "workflowTaskScheduledEventAttributes": { + "taskQueue": { + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", + "kind": "Sticky" + }, + "startToCloseTimeout": "10s", + "attempt": 1 + } + }, + { + "eventId": "17", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "WorkflowTaskStarted", + "version": "0", + "taskId": "5242920", + "workflowTaskStartedEventAttributes": { + "scheduledEventId": "16", + "identity": "1@0a5c2d703445", + "requestId": "e7f2dce5-1415-4740-addd-0ef0ae0ebdde" + } + }, + { + "eventId": "18", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "WorkflowTaskCompleted", + "version": "0", + "taskId": "5242923", + "workflowTaskCompletedEventAttributes": { + "scheduledEventId": "16", + "startedEventId": "17", + "identity": "1@0a5c2d703445", + "binaryChecksum": "" + } + }, + { + "eventId": "19", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "MarkerRecorded", + "version": "0", + "taskId": "5242924", + "markerRecordedEventAttributes": { + "details": { + "changeId": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "ImVuc3VyZV9jbGVhbl9qb2Jfc3RhdGUi" + } + ] + }, + "version": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "MQ==" + } + ] + } + }, + "markerName": "Version", + "workflowTaskCompletedEventId": "18" + } + }, + { + "eventId": "20", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "ActivityTaskScheduled", + "version": "0", + "taskId": "5242925", + "activityTaskScheduledEventAttributes": { + "activityId": "c35adb15-7871-33df-a773-5cd1d393a3f2", + "activityType": { + "name": "EnsureCleanJobState" + }, + "namespace": "", + "taskQueue": { + "name": "CONNECTION_UPDATER", + "kind": "Normal" + }, + "header": { + "fields": {} + }, + "input": { "payloads": [ { "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJ0aW1lVG9XYWl0IjoyMTIzMy4wMDAwMDAwMDB9" + "data": "eyJjb25uZWN0aW9uSWQiOiJiNjFkNWFjZC1iZjczLTRlZTctOGQ0Ni02OGY0MmQ5NTNmMjgifQ==" } ] }, - "scheduledEventId": "12", - "startedEventId": "13", - "identity": "1@airbyte-worker-74797b7bc-q4jfs" + "scheduleToCloseTimeout": "0s", + "scheduleToStartTimeout": "0s", + "startToCloseTimeout": "120s", + "heartbeatTimeout": "30s", + "workflowTaskCompletedEventId": "18", + "retryPolicy": { + "nonRetryableErrorTypes": [], + "initialInterval": "30s", + "backoffCoefficient": 2, + "maximumInterval": "600s", + "maximumAttempts": 5 + } } }, { - "eventId": "15", - "eventTime": "2022-07-19T15:29:55.079284934Z", + "eventId": "21", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "ActivityTaskStarted", + "version": "0", + "taskId": "5242929", + "activityTaskStartedEventAttributes": { + "scheduledEventId": "20", + "identity": "1@0a5c2d703445", + "requestId": "5c602db0-8eb8-44ab-8912-2c3ac72b9d22", + "attempt": 1 + } + }, + { + "eventId": "22", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "ActivityTaskCompleted", + "version": "0", + "taskId": "5242930", + "activityTaskCompletedEventAttributes": { + "scheduledEventId": "20", + "startedEventId": "21", + "identity": "1@0a5c2d703445" + } + }, + { + "eventId": "23", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "374953810", + "version": "0", + "taskId": "5242931", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-rm4tt:a529a431-c1ae-433e-911e-8e8b9fb3c930", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -332,62 +488,117 @@ } }, { - "eventId": "16", - "eventTime": "2022-07-19T15:29:55.094697045Z", + "eventId": "24", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "374953814", + "version": "0", + "taskId": "5242935", "workflowTaskStartedEventAttributes": { - "scheduledEventId": "15", - "identity": "a529a431-c1ae-433e-911e-8e8b9fb3c930", - "requestId": "df4c616e-8030-4c6b-b30a-3515893eaf82" + "scheduledEventId": "23", + "identity": "1@0a5c2d703445", + "requestId": "0d79c088-92d2-4e3a-8898-60f05eeaa357" } }, { - "eventId": "17", - "eventTime": "2022-07-19T15:29:55.154359336Z", + "eventId": "25", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "374953817", + "version": "0", + "taskId": "5242938", "workflowTaskCompletedEventAttributes": { - "scheduledEventId": "15", - "startedEventId": "16", - "identity": "1@airbyte-worker-74797b7bc-rm4tt", + "scheduledEventId": "23", + "startedEventId": "24", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { - "eventId": "18", - "eventTime": "2022-07-19T15:29:55.154414636Z", - "eventType": "TimerStarted", - "version": "1011", - "taskId": "374953818", - "timerStartedEventAttributes": { - "timerId": "04c4cd99-cbf6-3ec7-8ba2-2208e50d486a", - "startToFireTimeout": "21233s", - "workflowTaskCompletedEventId": "17" + "eventId": "26", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "ActivityTaskScheduled", + "version": "0", + "taskId": "5242939", + "activityTaskScheduledEventAttributes": { + "activityId": "6eaed398-e7b1-3066-90e6-9670c5179b16", + "activityType": { + "name": "GetTimeToWait" + }, + "namespace": "", + "taskQueue": { + "name": "CONNECTION_UPDATER", + "kind": "Normal" + }, + "header": { + "fields": {} + }, + "input": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "eyJjb25uZWN0aW9uSWQiOiJiNjFkNWFjZC1iZjczLTRlZTctOGQ0Ni02OGY0MmQ5NTNmMjgifQ==" + } + ] + }, + "scheduleToCloseTimeout": "0s", + "scheduleToStartTimeout": "0s", + "startToCloseTimeout": "120s", + "heartbeatTimeout": "30s", + "workflowTaskCompletedEventId": "25", + "retryPolicy": { + "nonRetryableErrorTypes": [], + "initialInterval": "30s", + "backoffCoefficient": 2, + "maximumInterval": "600s", + "maximumAttempts": 5 + } } }, { - "eventId": "19", - "eventTime": "2022-07-19T21:23:48.165680255Z", - "eventType": "TimerFired", - "version": "1011", - "taskId": "375402951", - "timerFiredEventAttributes": { - "timerId": "04c4cd99-cbf6-3ec7-8ba2-2208e50d486a", - "startedEventId": "18" + "eventId": "27", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "ActivityTaskStarted", + "version": "0", + "taskId": "5242943", + "activityTaskStartedEventAttributes": { + "scheduledEventId": "26", + "identity": "1@0a5c2d703445", + "requestId": "65c5785f-8c9f-4866-ac41-9768f3f2fb40", + "attempt": 1 } }, { - "eventId": "20", - "eventTime": "2022-07-19T21:23:48.165684295Z", + "eventId": "28", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "ActivityTaskCompleted", + "version": "0", + "taskId": "5242944", + "activityTaskCompletedEventAttributes": { + "result": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "eyJ0aW1lVG9XYWl0IjowLjB9" + } + ] + }, + "scheduledEventId": "26", + "startedEventId": "27", + "identity": "1@0a5c2d703445" + } + }, + { + "eventId": "29", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "375402952", + "version": "0", + "taskId": "5242945", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-rm4tt:a529a431-c1ae-433e-911e-8e8b9fb3c930", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -395,38 +606,38 @@ } }, { - "eventId": "21", - "eventTime": "2022-07-19T21:23:48.182808403Z", + "eventId": "30", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "375402956", + "version": "0", + "taskId": "5242949", "workflowTaskStartedEventAttributes": { - "scheduledEventId": "20", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", - "requestId": "745d964e-7dab-4075-8607-8e4d89df8ed1" + "scheduledEventId": "29", + "identity": "1@0a5c2d703445", + "requestId": "9913e78f-ecb6-4440-8375-734141f8d6b5" } }, { - "eventId": "22", - "eventTime": "2022-07-19T21:23:48.296543457Z", + "eventId": "31", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "375402960", + "version": "0", + "taskId": "5242952", "workflowTaskCompletedEventAttributes": { - "scheduledEventId": "20", - "startedEventId": "21", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", + "scheduledEventId": "29", + "startedEventId": "30", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { - "eventId": "23", - "eventTime": "2022-07-19T21:23:48.296567767Z", + "eventId": "32", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskScheduled", - "version": "1011", - "taskId": "375402961", + "version": "0", + "taskId": "5242953", "activityTaskScheduledEventAttributes": { - "activityId": "2cbdf9a5-6d31-31c5-8f20-41a22df2538f", + "activityId": "c15548c7-e8a9-3334-a035-6470179bf71f", "activityType": { "name": "CreateNewJob" }, @@ -444,7 +655,7 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJjb25uZWN0aW9uSWQiOiJkZjdlNjg4Ni00NTgxLTQwZWUtYmFlNS05ZTdmZjI3MWRhM2MifQ==" + "data": "eyJjb25uZWN0aW9uSWQiOiJiNjFkNWFjZC1iZjczLTRlZTctOGQ0Ni02OGY0MmQ5NTNmMjgifQ==" } ] }, @@ -452,36 +663,35 @@ "scheduleToStartTimeout": "0s", "startToCloseTimeout": "120s", "heartbeatTimeout": "30s", - "workflowTaskCompletedEventId": "22", + "workflowTaskCompletedEventId": "31", "retryPolicy": { + "nonRetryableErrorTypes": [], "initialInterval": "30s", "backoffCoefficient": 2, "maximumInterval": "600s", - "maximumAttempts": 5, - "nonRetryableErrorTypes": [] + "maximumAttempts": 5 } } }, { - "eventId": "24", - "eventTime": "2022-07-19T21:23:48.312296244Z", + "eventId": "33", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskStarted", - "version": "1011", - "taskId": "375402966", + "version": "0", + "taskId": "5242957", "activityTaskStartedEventAttributes": { - "scheduledEventId": "23", - "identity": "1@airbyte-worker-74797b7bc-zqmsb", - "requestId": "0bf1ffe0-752c-4e4e-917b-61d0f2f51d41", - "attempt": 1, - "lastFailure": null + "scheduledEventId": "32", + "identity": "1@0a5c2d703445", + "requestId": "9f2b2357-6c99-4e02-8fc6-df0d3ee71bb9", + "attempt": 1 } }, { - "eventId": "25", - "eventTime": "2022-07-19T21:23:48.664125740Z", + "eventId": "34", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskCompleted", - "version": "1011", - "taskId": "375402967", + "version": "0", + "taskId": "5242958", "activityTaskCompletedEventAttributes": { "result": { "payloads": [ @@ -489,24 +699,24 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJqb2JJZCI6Mzc5NTY3fQ==" + "data": "eyJqb2JJZCI6MX0=" } ] }, - "scheduledEventId": "23", - "startedEventId": "24", - "identity": "1@airbyte-worker-74797b7bc-zqmsb" + "scheduledEventId": "32", + "startedEventId": "33", + "identity": "1@0a5c2d703445" } }, { - "eventId": "26", - "eventTime": "2022-07-19T21:23:48.664131811Z", + "eventId": "35", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "375402968", + "version": "0", + "taskId": "5242959", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-gjw7f:0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -514,73 +724,71 @@ } }, { - "eventId": "27", - "eventTime": "2022-07-19T21:23:48.679367733Z", + "eventId": "36", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "375402972", + "version": "0", + "taskId": "5242963", "workflowTaskStartedEventAttributes": { - "scheduledEventId": "26", - "identity": "0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", - "requestId": "cf30c275-000e-46eb-aeab-cf0bd03287ed" + "scheduledEventId": "35", + "identity": "1@0a5c2d703445", + "requestId": "069caefc-7a14-4875-bba9-010a88051ffb" } }, { - "eventId": "28", - "eventTime": "2022-07-19T21:23:48.738681322Z", + "eventId": "37", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "375402976", + "version": "0", + "taskId": "5242966", "workflowTaskCompletedEventAttributes": { - "scheduledEventId": "26", - "startedEventId": "27", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", + "scheduledEventId": "35", + "startedEventId": "36", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { - "eventId": "29", - "eventTime": "2022-07-19T21:23:48.738710263Z", + "eventId": "38", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "MarkerRecorded", - "version": "1011", - "taskId": "375402977", + "version": "0", + "taskId": "5242967", "markerRecordedEventAttributes": { - "markerName": "Version", "details": { - "changeId": { + "version": { "payloads": [ { "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "InJlbmFtZV9hdHRlbXB0X2lkX3RvX251bWJlciI=" + "data": "MQ==" } ] }, - "version": { + "changeId": { "payloads": [ { "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "MQ==" + "data": "InJlbmFtZV9hdHRlbXB0X2lkX3RvX251bWJlciI=" } ] } }, - "workflowTaskCompletedEventId": "28", - "header": null, - "failure": null + "markerName": "Version", + "workflowTaskCompletedEventId": "37" } }, { - "eventId": "30", - "eventTime": "2022-07-19T21:23:48.738724093Z", + "eventId": "39", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskScheduled", - "version": "1011", - "taskId": "375402978", + "version": "0", + "taskId": "5242968", "activityTaskScheduledEventAttributes": { - "activityId": "81e28460-06d8-301e-ad50-cab2a726b83b", + "activityId": "bd698e91-962e-3014-a562-457fad4b1c1f", "activityType": { "name": "CreateNewAttemptNumber" }, @@ -598,7 +806,7 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJqb2JJZCI6Mzc5NTY3fQ==" + "data": "eyJqb2JJZCI6MX0=" } ] }, @@ -606,36 +814,35 @@ "scheduleToStartTimeout": "0s", "startToCloseTimeout": "120s", "heartbeatTimeout": "30s", - "workflowTaskCompletedEventId": "28", + "workflowTaskCompletedEventId": "37", "retryPolicy": { + "nonRetryableErrorTypes": [], "initialInterval": "30s", "backoffCoefficient": 2, "maximumInterval": "600s", - "maximumAttempts": 5, - "nonRetryableErrorTypes": [] + "maximumAttempts": 5 } } }, { - "eventId": "31", - "eventTime": "2022-07-19T21:23:48.755780009Z", + "eventId": "40", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskStarted", - "version": "1011", - "taskId": "375402983", + "version": "0", + "taskId": "5242972", "activityTaskStartedEventAttributes": { - "scheduledEventId": "30", - "identity": "1@airbyte-worker-74797b7bc-pcrvd", - "requestId": "b48f5264-4626-4413-8b02-0750d578aaaf", - "attempt": 1, - "lastFailure": null + "scheduledEventId": "39", + "identity": "1@0a5c2d703445", + "requestId": "f2dedaf6-4eb1-43a2-83bf-691937e714c3", + "attempt": 1 } }, { - "eventId": "32", - "eventTime": "2022-07-19T21:23:48.821849149Z", + "eventId": "41", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskCompleted", - "version": "1011", - "taskId": "375402984", + "version": "0", + "taskId": "5242973", "activityTaskCompletedEventAttributes": { "result": { "payloads": [ @@ -647,20 +854,20 @@ } ] }, - "scheduledEventId": "30", - "startedEventId": "31", - "identity": "1@airbyte-worker-74797b7bc-pcrvd" + "scheduledEventId": "39", + "startedEventId": "40", + "identity": "1@0a5c2d703445" } }, { - "eventId": "33", - "eventTime": "2022-07-19T21:23:48.821859909Z", + "eventId": "42", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "375402985", + "version": "0", + "taskId": "5242974", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-gjw7f:0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -668,38 +875,38 @@ } }, { - "eventId": "34", - "eventTime": "2022-07-19T21:23:48.838081860Z", + "eventId": "43", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "375402989", + "version": "0", + "taskId": "5242978", "workflowTaskStartedEventAttributes": { - "scheduledEventId": "33", - "identity": "0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", - "requestId": "ca6ffdfb-3728-4b0e-85dc-105a3701a0de" + "scheduledEventId": "42", + "identity": "1@0a5c2d703445", + "requestId": "058d8c63-59c3-4189-9ef7-f2499150f9cd" } }, { - "eventId": "35", - "eventTime": "2022-07-19T21:23:48.901446260Z", + "eventId": "44", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "375402993", + "version": "0", + "taskId": "5242981", "workflowTaskCompletedEventAttributes": { - "scheduledEventId": "33", - "startedEventId": "34", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", + "scheduledEventId": "42", + "startedEventId": "43", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { - "eventId": "36", - "eventTime": "2022-07-19T21:23:48.901490371Z", + "eventId": "45", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskScheduled", - "version": "1011", - "taskId": "375402994", + "version": "0", + "taskId": "5242982", "activityTaskScheduledEventAttributes": { - "activityId": "37a7ad09-ff1d-3dc6-940c-7534aa0b6e5c", + "activityId": "15678704-31bb-3dee-95f6-8db6621329a6", "activityType": { "name": "GetSyncWorkflowInputWithAttemptNumber" }, @@ -717,7 +924,7 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJhdHRlbXB0TnVtYmVyIjowLCJqb2JJZCI6Mzc5NTY3fQ==" + "data": "eyJhdHRlbXB0TnVtYmVyIjowLCJqb2JJZCI6MX0=" } ] }, @@ -725,36 +932,35 @@ "scheduleToStartTimeout": "0s", "startToCloseTimeout": "120s", "heartbeatTimeout": "30s", - "workflowTaskCompletedEventId": "35", + "workflowTaskCompletedEventId": "44", "retryPolicy": { + "nonRetryableErrorTypes": [], "initialInterval": "30s", "backoffCoefficient": 2, "maximumInterval": "600s", - "maximumAttempts": 5, - "nonRetryableErrorTypes": [] + "maximumAttempts": 5 } } }, { - "eventId": "37", - "eventTime": "2022-07-19T21:23:48.916840487Z", + "eventId": "46", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskStarted", - "version": "1011", - "taskId": "375402999", + "version": "0", + "taskId": "5242986", "activityTaskStartedEventAttributes": { - "scheduledEventId": "36", - "identity": "1@airbyte-worker-74797b7bc-8r2n9", - "requestId": "bd8297db-0f08-4205-841b-89b45ac97782", - "attempt": 1, - "lastFailure": null + "scheduledEventId": "45", + "identity": "1@0a5c2d703445", + "requestId": "24abad45-25e5-444c-9a08-593d47793539", + "attempt": 1 } }, { - "eventId": "38", - "eventTime": "2022-07-19T21:23:49.023417778Z", + "eventId": "47", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskCompleted", - "version": "1011", - "taskId": "375403000", + "version": "0", + "taskId": "5242987", "activityTaskCompletedEventAttributes": { "result": { "payloads": [ @@ -762,24 +968,24 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJqb2JSdW5Db25maWciOnsiam9iSWQiOiIzNzk1NjciLCJhdHRlbXB0SWQiOjB9LCJzb3VyY2VMYXVuY2hlckNvbmZpZyI6eyJqb2JJZCI6IjM3OTU2NyIsImF0dGVtcHRJZCI6MCwiZG9ja2VySW1hZ2UiOiJhaXJieXRlL3NvdXJjZS1yZWNoYXJnZTowLjEuNSJ9LCJkZXN0aW5hdGlvbkxhdW5jaGVyQ29uZmlnIjp7ImpvYklkIjoiMzc5NTY3IiwiYXR0ZW1wdElkIjowLCJkb2NrZXJJbWFnZSI6ImFpcmJ5dGUvZGVzdGluYXRpb24tYmlncXVlcnk6MS4xLjExIn0sInN5bmNJbnB1dCI6eyJuYW1lc3BhY2VEZWZpbml0aW9uIjoiZGVzdGluYXRpb24iLCJuYW1lc3BhY2VGb3JtYXQiOiIke1NPVVJDRV9OQU1FU1BBQ0V9IiwicHJlZml4IjoidGhldGlzdGVhX3JlY2hhcmdlXyIsInNvdXJjZUNvbmZpZ3VyYXRpb24iOnsic3RhcnRfZGF0ZSI6IjIwMTgtMTItMTNUMDA6MDA6MDBaIiwiYWNjZXNzX3Rva2VuIjp7Il9zZWNyZXQiOiJhaXJieXRlX3dvcmtzcGFjZV8zNDYwYjczYi1mMTIzLTQ1YmQtOTJjZC0xNGY1ZTMxYzQwY2Rfc2VjcmV0X2UzZDA1NDdlLWE3NWItNGIwNS1hMWE3LWIwNTg5ZjliNzQxNl92MSJ9fSwiZGVzdGluYXRpb25Db25maWd1cmF0aW9uIjp7ImRhdGFzZXRfaWQiOiJhaXJieXRlX21hc3Rlcl9yYXciLCJwcm9qZWN0X2lkIjoiY3VzdG9tZXJzLW1hbmFnZWQiLCJsb2FkaW5nX21ldGhvZCI6eyJtZXRob2QiOiJHQ1MgU3RhZ2luZyIsImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiSE1BQ19LRVkiLCJobWFjX2tleV9zZWNyZXQiOnsiX3NlY3JldCI6ImFpcmJ5dGVfd29ya3NwYWNlXzM0NjBiNzNiLWYxMjMtNDViZC05MmNkLTE0ZjVlMzFjNDBjZF9zZWNyZXRfYzI5Zjg5NWEtMDNkNy00ZjI5LThjMWUtOGNjOWJmYzAwOWNhX3YxIn0sImhtYWNfa2V5X2FjY2Vzc19pZCI6eyJfc2VjcmV0IjoiYWlyYnl0ZV93b3Jrc3BhY2VfMzQ2MGI3M2ItZjEyMy00NWJkLTkyY2QtMTRmNWUzMWM0MGNkX3NlY3JldF9iNDU1MzUxMi0zMWQwLTRjOGQtOGFhOC05YmUyOGUyMmM5ZDBfdjEifX0sImdjc19idWNrZXRfbmFtZSI6InNtX2FpcmJ5dGVfc3RhZ2luZ19zeW5jIiwiZ2NzX2J1Y2tldF9wYXRoIjoidG1wX2luZ2VzdGlvbl9zdGFnaW5nIiwia2VlcF9maWxlc19pbl9nY3MtYnVja2V0IjoiRGVsZXRlIGFsbCB0bXAgZmlsZXMgZnJvbSBHQ1MifSwiY3JlZGVudGlhbHNfanNvbiI6eyJfc2VjcmV0IjoiYWlyYnl0ZV93b3Jrc3BhY2VfMzQ2MGI3M2ItZjEyMy00NWJkLTkyY2QtMTRmNWUzMWM0MGNkX3NlY3JldF84NzNhMWJlNC04ZWZjLTQ3N2ItYWE1Ny0xYmU5NmY0ZjJmNzBfdjEifSwiZGF0YXNldF9sb2NhdGlvbiI6IlVTIiwidHJhbnNmb3JtYXRpb25fcHJpb3JpdHkiOiJpbnRlcmFjdGl2ZSIsImJpZ19xdWVyeV9jbGllbnRfYnVmZmVyX3NpemVfbWIiOjE1fSwib3BlcmF0aW9uU2VxdWVuY2UiOlt7Im9wZXJhdGlvbklkIjoiNWMxYjgwNWQtNzAzMC00NWIzLTliNjMtNDliZWExMDM1NTBkIiwibmFtZSI6Ik5vcm1hbGl6YXRpb24iLCJvcGVyYXRvclR5cGUiOiJub3JtYWxpemF0aW9uIiwib3BlcmF0b3JOb3JtYWxpemF0aW9uIjp7Im9wdGlvbiI6ImJhc2ljIn0sInRvbWJzdG9uZSI6ZmFsc2UsIndvcmtzcGFjZUlkIjoiMzQ2MGI3M2ItZjEyMy00NWJkLTkyY2QtMTRmNWUzMWM0MGNkIn1dLCJjYXRhbG9nIjp7InN0cmVhbXMiOlt7InN0cmVhbSI6eyJuYW1lIjoiY2hhcmdlcyIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJub3RlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGFncyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInR5cGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJlbWFpbCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImVycm9yIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic3RhdHVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGF4X2xpbmVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyIsIm51bWJlciJdfSwidG90YWxfdGF4Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyIsIm51bWJlciJdfSwiYWRkcmVzc19pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiZXJyb3JfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJsaW5lX2l0ZW1zIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJyZXRyeV9kYXRlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInRvdGFsX3ByaWNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicHJvY2Vzc2VkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwic2NoZWR1bGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidG90YWxfd2VpZ2h0Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImN1c3RvbWVyX2hhc2giOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0b3RhbF9yZWZ1bmRzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYW5hbHl0aWNzX2RhdGEiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJjbGllbnRfZGV0YWlscyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXX0sImRpc2NvdW50X2NvZGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOiJvYmplY3QifX0sInByb2Nlc3Nvcl9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hpcHBpbmdfbGluZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6Im9iamVjdCJ9fSwic3VidG90YWxfcHJpY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0cmFuc2FjdGlvbl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ6aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjaXR5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicGhvbmUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY291bnRyeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFkZHJlc3MxIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcm92aW5jZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNhcnRfbm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImRpc2NvdW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5vdGVfYXR0cmlidXRlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic2hpcHBpbmdfbGluZXNfb3ZlcnJpZGUiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX19fSwibm90ZV9hdHRyaWJ1dGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJzaGlwbWVudHNfY291bnQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwidG90YWxfZGlzY291bnRzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hpcHBpbmdfYWRkcmVzcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ6aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjaXR5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicGhvbmUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY291bnRyeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFkZHJlc3MxIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcm92aW5jZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNhcnRfbm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImRpc2NvdW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5vdGVfYXR0cmlidXRlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic2hpcHBpbmdfbGluZXNfb3ZlcnJpZGUiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX19fSwic2hvcGlmeV9vcmRlcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm51bWJlcl90aW1lc190cmllZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJoYXNfdW5jb21taXRlZF9jaGFuZ2VzIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sInRvdGFsX2xpbmVfaXRlbXNfcHJpY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJsYXN0X2NoYXJnZV9hdHRlbXB0X2RhdGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJzaG9waWZ5X3ZhcmlhbnRfaWRfbm90X2ZvdW5kIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiLCJzdHJpbmciXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoiY29sbGVjdGlvbnMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifX19LCJzdXBwb3J0ZWRfc3luY19tb2RlcyI6WyJmdWxsX3JlZnJlc2giXSwiZGVmYXVsdF9jdXJzb3JfZmllbGQiOltdLCJzb3VyY2VfZGVmaW5lZF9wcmltYXJ5X2tleSI6W1siaWQiXV19LCJzeW5jX21vZGUiOiJmdWxsX3JlZnJlc2giLCJjdXJzb3JfZmllbGQiOltdLCJkZXN0aW5hdGlvbl9zeW5jX21vZGUiOiJvdmVyd3JpdGUiLCJwcmltYXJ5X2tleSI6W1siaWQiXV19LHsic3RyZWFtIjp7Im5hbWUiOiJjdXN0b21lcnMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiaGFzaCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImVtYWlsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic3RhdHVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiYmlsbGluZ196aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2NpdHkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX3Bob25lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYW5hbHl0aWNzX2RhdGEiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJwcm9jZXNzb3JfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfY29tcGFueSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfY291bnRyeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzczEiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2FkZHJlc3MyIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19wcm92aW5jZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFjY2VwdHNfbWFya2V0aW5nIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImJpbGxpbmdfbGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19maXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hvcGlmeV9jdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm51bWJlcl9zdWJzY3JpcHRpb25zIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInN0cmlwZV9jdXN0b21lcl90b2tlbiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImhhc192YWxpZF9wYXltZW50X21ldGhvZCI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJmaXJzdF9jaGFyZ2VfcHJvY2Vzc2VkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiaGFzX2NhcmRfZXJyb3JfaW5fZHVubmluZyI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJudW1iZXJfYWN0aXZlX3N1YnNjcmlwdGlvbnMiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwicmVhc29uX3BheW1lbnRfbWV0aG9kX25vdF92YWxpZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoiZGlzY291bnRzIiwianNvbl9zY2hlbWEiOnsidHlwZSI6Im9iamVjdCIsIiRzY2hlbWEiOiJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImNvZGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ2YWx1ZSI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImVuZHNfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJkdXJhdGlvbiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInN0YXJ0c19hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImFwcGxpZXNfdG8iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidGltZXNfdXNlZCI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJ1c2FnZV9saW1pdCI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sImFwcGxpZXNfdG9faWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiZGlzY291bnRfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNoYW5uZWxfc2V0dGluZ3MiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsiYXBpIjp7InR5cGUiOiJvYmplY3QiLCJwcm9wZXJ0aWVzIjp7ImNhbl9hcHBseSI6eyJ0eXBlIjoiYm9vbGVhbiJ9fX0sImNoZWNrb3V0X3BhZ2UiOnsidHlwZSI6Im9iamVjdCIsInByb3BlcnRpZXMiOnsiY2FuX2FwcGx5Ijp7InR5cGUiOiJib29sZWFuIn19fSwiY3VzdG9tZXJfcG9ydGFsIjp7InR5cGUiOiJvYmplY3QiLCJwcm9wZXJ0aWVzIjp7ImNhbl9hcHBseSI6eyJ0eXBlIjoiYm9vbGVhbiJ9fX0sIm1lcmNoYW50X3BvcnRhbCI6eyJ0eXBlIjoib2JqZWN0IiwicHJvcGVydGllcyI6eyJjYW5fYXBwbHkiOnsidHlwZSI6ImJvb2xlYW4ifX19fX0sIm9uY2VfcGVyX2N1c3RvbWVyIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImFwcGxpZXNfdG9fcmVzb3VyY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJkdXJhdGlvbl91c2FnZV9saW1pdCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJleHRlcm5hbF9kaXNjb3VudF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFwcGxpZXNfdG9fcHJvZHVjdF90eXBlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZXh0ZXJuYWxfZGlzY291bnRfc291cmNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicHJlcmVxdWlzaXRlX3N1YnRvdGFsX21pbiI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sImZpcnN0X3RpbWVfY3VzdG9tZXJfcmVzdHJpY3Rpb24iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sInN1cHBvcnRlZF9zeW5jX21vZGVzIjpbImZ1bGxfcmVmcmVzaCIsImluY3JlbWVudGFsIl0sInNvdXJjZV9kZWZpbmVkX2N1cnNvciI6dHJ1ZSwiZGVmYXVsdF9jdXJzb3JfZmllbGQiOlsidXBkYXRlZF9hdCJdLCJzb3VyY2VfZGVmaW5lZF9wcmltYXJ5X2tleSI6W1siaWQiXV19LCJzeW5jX21vZGUiOiJpbmNyZW1lbnRhbCIsImN1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sImRlc3RpbmF0aW9uX3N5bmNfbW9kZSI6ImFwcGVuZF9kZWR1cCIsInByaW1hcnlfa2V5IjpbWyJpZCJdXX0seyJzdHJlYW0iOnsibmFtZSI6Im9uZXRpbWVzIiwianNvbl9zY2hlbWEiOnsidHlwZSI6Im9iamVjdCIsIiRzY2hlbWEiOiJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInNrdSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByaWNlIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiLCJzdHJpbmciLCJudW1iZXIiXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInF1YW50aXR5Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImFkZHJlc3NfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciIsInN0cmluZyJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInByb3BlcnRpZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJjdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIiwic3RyaW5nIl19LCJwcm9kdWN0X3RpdGxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidmFyaWFudF90aXRsZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInNob3BpZnlfcHJvZHVjdF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzaG9waWZ5X3ZhcmlhbnRfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwicmVjaGFyZ2VfcHJvZHVjdF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJuZXh0X2NoYXJnZV9zY2hlZHVsZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9fX0sInN1cHBvcnRlZF9zeW5jX21vZGVzIjpbImZ1bGxfcmVmcmVzaCIsImluY3JlbWVudGFsIl0sInNvdXJjZV9kZWZpbmVkX2N1cnNvciI6dHJ1ZSwiZGVmYXVsdF9jdXJzb3JfZmllbGQiOlsidXBkYXRlZF9hdCJdLCJzb3VyY2VfZGVmaW5lZF9wcmltYXJ5X2tleSI6W1siaWQiXV19LCJzeW5jX21vZGUiOiJpbmNyZW1lbnRhbCIsImN1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sImRlc3RpbmF0aW9uX3N5bmNfbW9kZSI6ImFwcGVuZF9kZWR1cCIsInByaW1hcnlfa2V5IjpbWyJpZCJdXX0seyJzdHJlYW0iOnsibmFtZSI6Im9yZGVycyIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJoYXNoIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInRhZ3MiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0eXBlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZW1haWwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzdGF0dXMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjdXJyZW5jeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImN1c3RvbWVyIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImhhc2giOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJlbWFpbCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImJpbGxpbmdfemlwIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19jaXR5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19waG9uZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByb2Nlc3Nvcl90eXBlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19jb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19jb3VudHJ5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19hZGRyZXNzMSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX3Byb3ZpbmNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19sYXN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2ZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaG9waWZ5X2N1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibnVtYmVyX3N1YnNjcmlwdGlvbnMiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiaGFzX3ZhbGlkX3BheW1lbnRfbWV0aG9kIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImZpcnN0X2NoYXJnZV9wcm9jZXNzZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJoYXNfY2FyZF9lcnJvcl9pbl9kdW5uaW5nIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sIm51bWJlcl9hY3RpdmVfc3Vic2NyaXB0aW9ucyI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJyZWFzb25fcGF5bWVudF9tZXRob2Rfbm90X3ZhbGlkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJyZWRhY3RlZCI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJjaGFyZ2VfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGF4X2xpbmVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJ0b3RhbF90YXgiOnsidHlwZSI6WyJudWxsIiwibnVtYmVyIiwic3RyaW5nIl19LCJhZGRyZXNzX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImJyb3dzZXJfaXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiZmlyc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImlzX3ByZXBhaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibGluZV9pdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjoib2JqZWN0IiwicHJvcGVydGllcyI6eyJza3UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJncmFtcyI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sInByaWNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGl0bGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJpbWFnZXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJxdWFudGl0eSI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJwcm9wZXJ0aWVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJ2YXJpYW50X3RpdGxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic3Vic2NyaXB0aW9uX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInNob3BpZnlfcHJvZHVjdF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciLCJpbnRlZ2VyIl19LCJzaG9waWZ5X3ZhcmlhbnRfaWQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIiwiaW50ZWdlciJdfSwiZXh0ZXJuYWxfaW52ZW50b3J5X3BvbGljeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJjdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ0b3RhbF9wcmljZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByb2Nlc3NlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInNjaGVkdWxlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInNoaXBwZWRfZGF0ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInRvdGFsX3dlaWdodCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJjaGFyZ2Vfc3RhdHVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidG90YWxfcmVmdW5kcyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImRpc2NvdW50X2NvZGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJzaGlwcGluZ19saW5lcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic3VidG90YWxfcHJpY2UiOnsidHlwZSI6WyJudWxsIiwibnVtYmVyIiwic3RyaW5nIl19LCJ0cmFuc2FjdGlvbl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ6aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjaXR5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicGhvbmUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY291bnRyeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFkZHJlc3MxIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcm92aW5jZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNhcnRfbm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImRpc2NvdW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5vdGVfYXR0cmlidXRlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic2hpcHBpbmdfbGluZXNfb3ZlcnJpZGUiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX19fSwibm90ZV9hdHRyaWJ1dGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJ0b3RhbF9kaXNjb3VudHMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaGlwcGluZ19hZGRyZXNzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInppcCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNpdHkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwaG9uZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNvbXBhbnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb3VudHJ5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczEiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJhZGRyZXNzMiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByb3ZpbmNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY2FydF9ub3RlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiY3VzdG9tZXJfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiZGlzY291bnRfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibm90ZV9hdHRyaWJ1dGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJzaGlwcGluZ19saW5lc19vdmVycmlkZSI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfX19LCJzaG9waWZ5X29yZGVyX2lkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzc19pc19hY3RpdmUiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwicGF5bWVudF9wcm9jZXNzb3IiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaG9waWZ5X2NhcnRfdG9rZW4iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaG9waWZ5X2N1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hvcGlmeV9vcmRlcl9udW1iZXIiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwidG90YWxfbGluZV9pdGVtc19wcmljZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoicHJvZHVjdHMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwidGl0bGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJoYW5kbGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJpbWFnZXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImNvbGxlY3Rpb25faWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiZGlzY291bnRfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImRpc2NvdW50X2Ftb3VudCI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sIm9yZGVyX2RheV9vZl93ZWVrIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImN1dG9mZl9kYXlfb2Zfd2VlayI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJvcmRlcl9kYXlfb2ZfbW9udGgiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic2hvcGlmeV9wcm9kdWN0X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImN1dG9mZl9kYXlfb2ZfbW9udGgiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwib3JkZXJfaW50ZXJ2YWxfdW5pdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm1vZGlmaWFibGVfcHJvcGVydGllcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic3Vic2NyaXB0aW9uX2RlZmF1bHRzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdfSwiY2hhcmdlX2ludGVydmFsX2ZyZXF1ZW5jeSI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzdG9yZWZyb250X3B1cmNoYXNlX29wdGlvbnMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJvcmRlcl9pbnRlcnZhbF9mcmVxdWVuY3lfb3B0aW9ucyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwiZXhwaXJlX2FmdGVyX3NwZWNpZmljX251bWJlcl9vZl9jaGFyZ2VzIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIl0sImRlZmF1bHRfY3Vyc29yX2ZpZWxkIjpbXSwic291cmNlX2RlZmluZWRfcHJpbWFyeV9rZXkiOltbImlkIl1dfSwic3luY19tb2RlIjoiZnVsbF9yZWZyZXNoIiwiY3Vyc29yX2ZpZWxkIjpbXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoib3ZlcndyaXRlIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoic2hvcCIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJzaG9wIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdfSwic3RvcmUiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19fX0sInN1cHBvcnRlZF9zeW5jX21vZGVzIjpbImZ1bGxfcmVmcmVzaCJdLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6W10sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJzaG9wIl0sWyJzdG9yZSJdXX0sInN5bmNfbW9kZSI6ImZ1bGxfcmVmcmVzaCIsImN1cnNvcl9maWVsZCI6W10sImRlc3RpbmF0aW9uX3N5bmNfbW9kZSI6Im92ZXJ3cml0ZSIsInByaW1hcnlfa2V5IjpbWyJzaG9wIl0sWyJzdG9yZSJdXX0seyJzdHJlYW0iOnsibmFtZSI6InN1YnNjcmlwdGlvbnMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic2t1Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZW1haWwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcmljZSI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInF1YW50aXR5Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImFkZHJlc3NfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImlzX3ByZXBhaWQiOnsidHlwZSI6WyJudWxsIiwiYm9vbGVhbiJdfSwicHJvcGVydGllcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjoib2JqZWN0IiwicHJvcGVydGllcyI6eyJuYW1lIjp7InR5cGUiOiJzdHJpbmcifSwidmFsdWUiOnsidHlwZSI6WyJzdHJpbmciLCJpbnRlZ2VyIl19fX19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiY3VzdG9tZXJfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiY2FuY2VsbGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiaXNfc2tpcHBhYmxlIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImlzX3N3YXBwYWJsZSI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJza3Vfb3ZlcnJpZGUiOnsidHlwZSI6WyJudWxsIiwiYm9vbGVhbiJdfSwiY29tbWl0X3VwZGF0ZSI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJwcm9kdWN0X3RpdGxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidmFyaWFudF90aXRsZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFuYWx5dGljc19kYXRhIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdfSwib3JkZXJfZGF5X29mX3dlZWsiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiaGFzX3F1ZXVlZF9jaGFyZ2VzIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm9yZGVyX2RheV9vZl9tb250aCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzaG9waWZ5X3Byb2R1Y3RfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic2hvcGlmeV92YXJpYW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImNhbmNlbGxhdGlvbl9yZWFzb24iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJtYXhfcmV0cmllc19yZWFjaGVkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm9yZGVyX2ludGVydmFsX3VuaXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJyZWNoYXJnZV9wcm9kdWN0X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5leHRfY2hhcmdlX3NjaGVkdWxlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm9yZGVyX2ludGVydmFsX2ZyZXF1ZW5jeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNoYXJnZV9pbnRlcnZhbF9mcmVxdWVuY3kiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjYW5jZWxsYXRpb25fcmVhc29uX2NvbW1lbnRzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZXhwaXJlX2FmdGVyX3NwZWNpZmljX251bWJlcl9vZl9jaGFyZ2VzIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfV19LCJzdGF0ZSI6eyJzdGF0ZSI6eyJvcmRlcnMiOnsidXBkYXRlZF9hdCI6IjIwMjItMDctMTlUMDA6MDg6MDgifSwiY2hhcmdlcyI6eyJ1cGRhdGVkX2F0IjoiMjAyMi0wNy0xOVQwMDowODowOSJ9LCJvbmV0aW1lcyI6eyJ1cGRhdGVkX2F0IjoiMjAyMi0wMy0yMlQxODoxMTozMCJ9LCJjdXN0b21lcnMiOnsidXBkYXRlZF9hdCI6IjIwMjItMDctMTlUMDA6MTI6MzIifSwiZGlzY291bnRzIjp7InVwZGF0ZWRfYXQiOiIyMDIyLTA3LTE4VDIyOjA3OjE0In0sInN1YnNjcmlwdGlvbnMiOnsidXBkYXRlZF9hdCI6IjIwMjItMDctMTlUMDA6MDg6MDcifX19LCJyZXNvdXJjZVJlcXVpcmVtZW50cyI6eyJjcHVfcmVxdWVzdCI6IjAuNSIsImNwdV9saW1pdCI6IjAuNSIsIm1lbW9yeV9yZXF1ZXN0IjoiNTAwTWkiLCJtZW1vcnlfbGltaXQiOiI1MDBNaSJ9LCJzb3VyY2VSZXNvdXJjZVJlcXVpcmVtZW50cyI6eyJjcHVfcmVxdWVzdCI6IjAuNSIsImNwdV9saW1pdCI6IjAuNSIsIm1lbW9yeV9yZXF1ZXN0IjoiNTAwTWkiLCJtZW1vcnlfbGltaXQiOiI1MDBNaSJ9LCJkZXN0aW5hdGlvblJlc291cmNlUmVxdWlyZW1lbnRzIjp7ImNwdV9yZXF1ZXN0IjoiMC41IiwiY3B1X2xpbWl0IjoiMC41IiwibWVtb3J5X3JlcXVlc3QiOiIxR2kiLCJtZW1vcnlfbGltaXQiOiIxR2kifX19" + "data": "eyJqb2JSdW5Db25maWciOnsiam9iSWQiOiIxIiwiYXR0ZW1wdElkIjowfSwic291cmNlTGF1bmNoZXJDb25maWciOnsiam9iSWQiOiIxIiwiYXR0ZW1wdElkIjowLCJkb2NrZXJJbWFnZSI6ImFpcmJ5dGUvc291cmNlLXBva2VhcGk6MC4xLjUiLCJzdXBwb3J0c0RidCI6ZmFsc2UsInByb3RvY29sVmVyc2lvbiI6eyJ2ZXJzaW9uIjoiMC4yLjAifSwiaXNDdXN0b21Db25uZWN0b3IiOmZhbHNlfSwiZGVzdGluYXRpb25MYXVuY2hlckNvbmZpZyI6eyJqb2JJZCI6IjEiLCJhdHRlbXB0SWQiOjAsImRvY2tlckltYWdlIjoiYWlyYnl0ZS9kZXN0aW5hdGlvbi1lMmUtdGVzdDowLjIuNCIsInN1cHBvcnRzRGJ0IjpmYWxzZSwicHJvdG9jb2xWZXJzaW9uIjp7InZlcnNpb24iOiIwLjIuMCJ9LCJpc0N1c3RvbUNvbm5lY3RvciI6ZmFsc2V9LCJzeW5jSW5wdXQiOnsibmFtZXNwYWNlRGVmaW5pdGlvbiI6ImRlc3RpbmF0aW9uIiwibmFtZXNwYWNlRm9ybWF0IjoiJHtTT1VSQ0VfTkFNRVNQQUNFfSIsInByZWZpeCI6IiIsInNvdXJjZUNvbmZpZ3VyYXRpb24iOnsicG9rZW1vbl9uYW1lIjoiZGl0dG8ifSwiZGVzdGluYXRpb25Db25maWd1cmF0aW9uIjp7InR5cGUiOiJMT0dHSU5HIiwibG9nZ2luZ19jb25maWciOnsibG9nZ2luZ190eXBlIjoiRmlyc3ROIiwibWF4X2VudHJ5X2NvdW50IjoxMDB9fSwib3BlcmF0aW9uU2VxdWVuY2UiOltdLCJjYXRhbG9nIjp7InN0cmVhbXMiOlt7InN0cmVhbSI6eyJuYW1lIjoicG9rZW1vbiIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZm9ybXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fX0sIm1vdmVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7Im1vdmUiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwidmVyc2lvbl9ncm91cF9kZXRhaWxzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InZlcnNpb25fZ3JvdXAiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwibGV2ZWxfbGVhcm5lZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJtb3ZlX2xlYXJuX21ldGhvZCI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ1cmwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19fX19fX19LCJvcmRlciI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzdGF0cyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJzdGF0Ijp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sImVmZm9ydCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJiYXNlX3N0YXQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfX19fSwidHlwZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsic2xvdCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ0eXBlIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX19fX0sImhlaWdodCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ3ZWlnaHQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic3BlY2llcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ1cmwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJzcHJpdGVzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7ImJhY2tfc2hpbnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiYWNrX2ZlbWFsZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImZyb250X3NoaW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmFja19kZWZhdWx0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZnJvbnRfZmVtYWxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZnJvbnRfZGVmYXVsdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJhY2tfc2hpbnlfZmVtYWxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZnJvbnRfc2hpbnlfZmVtYWxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJhYmlsaXRpZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsic2xvdCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJhYmlsaXR5Ijp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sImlzX2hpZGRlbiI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19fX19LCJoZWxkX2l0ZW1zIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7Iml0ZW0iOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwidmVyc2lvbl9kZXRhaWxzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InJhcml0eSI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ2ZXJzaW9uIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX19fX19fX0sImlzX2RlZmF1bHQgIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImdhbWVfaW5kaWNlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ2ZXJzaW9uIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sImdhbWVfaW5kZXgiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfX19fSwiYmFzZV9leHBlcmllbmNlIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImxvY2F0aW9uX2FyZWFfZW5jb3VudGVycyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIl0sImRlZmF1bHRfY3Vyc29yX2ZpZWxkIjpbXSwic291cmNlX2RlZmluZWRfcHJpbWFyeV9rZXkiOltdfSwic3luY19tb2RlIjoiZnVsbF9yZWZyZXNoIiwiY3Vyc29yX2ZpZWxkIjpbXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoib3ZlcndyaXRlIiwicHJpbWFyeV9rZXkiOltdfV19LCJyZXNvdXJjZVJlcXVpcmVtZW50cyI6eyJjcHVfcmVxdWVzdCI6IiIsImNwdV9saW1pdCI6IiIsIm1lbW9yeV9yZXF1ZXN0IjoiIiwibWVtb3J5X2xpbWl0IjoiIn0sInNvdXJjZVJlc291cmNlUmVxdWlyZW1lbnRzIjp7ImNwdV9yZXF1ZXN0IjoiIiwiY3B1X2xpbWl0IjoiIiwibWVtb3J5X3JlcXVlc3QiOiIiLCJtZW1vcnlfbGltaXQiOiIifSwiZGVzdGluYXRpb25SZXNvdXJjZVJlcXVpcmVtZW50cyI6eyJjcHVfcmVxdWVzdCI6IiIsImNwdV9saW1pdCI6IiIsIm1lbW9yeV9yZXF1ZXN0IjoiIiwibWVtb3J5X2xpbWl0IjoiIn0sIndvcmtzcGFjZUlkIjoiOGRmNDE3ZTQtNmE1NC00MTcyLWFlNTEtZTEyNTQwN2RjYmVjIn19" } ] }, - "scheduledEventId": "36", - "startedEventId": "37", - "identity": "1@airbyte-worker-74797b7bc-8r2n9" + "scheduledEventId": "45", + "startedEventId": "46", + "identity": "1@0a5c2d703445" } }, { - "eventId": "39", - "eventTime": "2022-07-19T21:23:49.023424558Z", + "eventId": "48", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "375403001", + "version": "0", + "taskId": "5242988", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-gjw7f:0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -787,38 +993,38 @@ } }, { - "eventId": "40", - "eventTime": "2022-07-19T21:23:49.038735132Z", + "eventId": "49", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "375403005", + "version": "0", + "taskId": "5242992", "workflowTaskStartedEventAttributes": { - "scheduledEventId": "39", - "identity": "0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", - "requestId": "b5fb828f-9db2-4616-b123-e7dab09f6b6b" + "scheduledEventId": "48", + "identity": "1@0a5c2d703445", + "requestId": "e0a52870-5207-4481-b787-6e9eeb057323" } }, { - "eventId": "41", - "eventTime": "2022-07-19T21:23:49.142064527Z", + "eventId": "50", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "375403009", + "version": "0", + "taskId": "5242995", "workflowTaskCompletedEventAttributes": { - "scheduledEventId": "39", - "startedEventId": "40", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", + "scheduledEventId": "48", + "startedEventId": "49", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { - "eventId": "42", - "eventTime": "2022-07-19T21:23:49.142093308Z", + "eventId": "51", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskScheduled", - "version": "1011", - "taskId": "375403010", + "version": "0", + "taskId": "5242996", "activityTaskScheduledEventAttributes": { - "activityId": "a89eda17-7a1a-334c-a7ee-ed7004cc5259", + "activityId": "fde35dc8-b459-3e7f-92f6-0e40a68aa3b0", "activityType": { "name": "ReportJobStart" }, @@ -836,7 +1042,7 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJqb2JJZCI6Mzc5NTY3fQ==" + "data": "eyJqb2JJZCI6MSwiY29ubmVjdGlvbklkIjoiYjYxZDVhY2QtYmY3My00ZWU3LThkNDYtNjhmNDJkOTUzZjI4In0=" } ] }, @@ -844,52 +1050,50 @@ "scheduleToStartTimeout": "0s", "startToCloseTimeout": "120s", "heartbeatTimeout": "30s", - "workflowTaskCompletedEventId": "41", + "workflowTaskCompletedEventId": "50", "retryPolicy": { + "nonRetryableErrorTypes": [], "initialInterval": "30s", "backoffCoefficient": 2, "maximumInterval": "600s", - "maximumAttempts": 5, - "nonRetryableErrorTypes": [] + "maximumAttempts": 5 } } }, { - "eventId": "43", - "eventTime": "2022-07-19T21:23:49.158710631Z", + "eventId": "52", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskStarted", - "version": "1011", - "taskId": "375403015", + "version": "0", + "taskId": "5243000", "activityTaskStartedEventAttributes": { - "scheduledEventId": "42", - "identity": "1@airbyte-worker-74797b7bc-9hwf7", - "requestId": "7799cff4-7844-4696-9f8e-e64842899a05", - "attempt": 1, - "lastFailure": null + "scheduledEventId": "51", + "identity": "1@0a5c2d703445", + "requestId": "9aff9125-d8a0-4737-9e3c-57a2f671c57e", + "attempt": 1 } }, { - "eventId": "44", - "eventTime": "2022-07-19T21:23:49.244160866Z", + "eventId": "53", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskCompleted", - "version": "1011", - "taskId": "375403016", + "version": "0", + "taskId": "5243001", "activityTaskCompletedEventAttributes": { - "result": null, - "scheduledEventId": "42", - "startedEventId": "43", - "identity": "1@airbyte-worker-74797b7bc-9hwf7" + "scheduledEventId": "51", + "startedEventId": "52", + "identity": "1@0a5c2d703445" } }, { - "eventId": "45", - "eventTime": "2022-07-19T21:23:49.244166926Z", + "eventId": "54", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "375403017", + "version": "0", + "taskId": "5243002", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-gjw7f:0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -897,75 +1101,106 @@ } }, { - "eventId": "46", - "eventTime": "2022-07-19T21:23:49.260118939Z", + "eventId": "55", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "375403021", + "version": "0", + "taskId": "5243006", "workflowTaskStartedEventAttributes": { - "scheduledEventId": "45", - "identity": "0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", - "requestId": "cb0b4a76-a8c2-4eeb-a505-116892f3d3c6" + "scheduledEventId": "54", + "identity": "1@0a5c2d703445", + "requestId": "ff9232bc-5af8-4c50-ab1a-db7f5c650953" } }, { - "eventId": "47", - "eventTime": "2022-07-19T21:23:49.322523491Z", + "eventId": "56", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "375403025", + "version": "0", + "taskId": "5243009", "workflowTaskCompletedEventAttributes": { - "scheduledEventId": "45", - "startedEventId": "46", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", + "scheduledEventId": "54", + "startedEventId": "55", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { - "eventId": "48", - "eventTime": "2022-07-19T21:23:49.322547621Z", + "eventId": "57", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "MarkerRecorded", + "version": "0", + "taskId": "5243010", + "markerRecordedEventAttributes": { + "details": { + "version": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "MQ==" + } + ] + }, + "changeId": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "ImNoZWNrX2JlZm9yZV9zeW5jIg==" + } + ] + } + }, + "markerName": "Version", + "workflowTaskCompletedEventId": "56" + } + }, + { + "eventId": "58", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "MarkerRecorded", - "version": "1011", - "taskId": "375403026", + "version": "0", + "taskId": "5243011", "markerRecordedEventAttributes": { - "markerName": "Version", "details": { - "changeId": { + "version": { "payloads": [ { "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "ImNoZWNrX2JlZm9yZV9zeW5jIg==" + "data": "MQ==" } ] }, - "version": { + "changeId": { "payloads": [ { "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "MQ==" + "data": "ImNoZWNrX3ByZXZpb3VzX2pvYl9vcl9hdHRlbXB0Ig==" } ] } }, - "workflowTaskCompletedEventId": "47", - "header": null, - "failure": null + "markerName": "Version", + "workflowTaskCompletedEventId": "56" } }, { - "eventId": "49", - "eventTime": "2022-07-19T21:23:49.322559782Z", + "eventId": "59", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskScheduled", - "version": "1011", - "taskId": "375403027", + "version": "0", + "taskId": "5243012", "activityTaskScheduledEventAttributes": { - "activityId": "a50bf6b0-ac2a-3cba-8326-653876e97e4d", + "activityId": "e089c6a0-e7f4-310c-bbbf-edb5b47fa1f6", "activityType": { - "name": "Run" + "name": "IsLastJobOrAttemptFailure" }, "namespace": "", "taskQueue": { @@ -981,7 +1216,7 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJqb2JSdW5Db25maWciOnsiam9iSWQiOiIzNzk1NjciLCJhdHRlbXB0SWQiOjB9LCJsYXVuY2hlckNvbmZpZyI6eyJqb2JJZCI6IjM3OTU2NyIsImF0dGVtcHRJZCI6MCwiZG9ja2VySW1hZ2UiOiJhaXJieXRlL3NvdXJjZS1yZWNoYXJnZTowLjEuNSJ9LCJjb25uZWN0aW9uQ29uZmlndXJhdGlvbiI6eyJjb25uZWN0aW9uQ29uZmlndXJhdGlvbiI6eyJzdGFydF9kYXRlIjoiMjAxOC0xMi0xM1QwMDowMDowMFoiLCJhY2Nlc3NfdG9rZW4iOnsiX3NlY3JldCI6ImFpcmJ5dGVfd29ya3NwYWNlXzM0NjBiNzNiLWYxMjMtNDViZC05MmNkLTE0ZjVlMzFjNDBjZF9zZWNyZXRfZTNkMDU0N2UtYTc1Yi00YjA1LWExYTctYjA1ODlmOWI3NDE2X3YxIn19fX0=" + "data": "eyJqb2JJZCI6MSwiYXR0ZW1wdElkIjowLCJjb25uZWN0aW9uSWQiOiJiNjFkNWFjZC1iZjczLTRlZTctOGQ0Ni02OGY0MmQ5NTNmMjgifQ==" } ] }, @@ -989,36 +1224,35 @@ "scheduleToStartTimeout": "0s", "startToCloseTimeout": "120s", "heartbeatTimeout": "30s", - "workflowTaskCompletedEventId": "47", + "workflowTaskCompletedEventId": "56", "retryPolicy": { + "nonRetryableErrorTypes": [], "initialInterval": "30s", "backoffCoefficient": 2, "maximumInterval": "600s", - "maximumAttempts": 5, - "nonRetryableErrorTypes": [] + "maximumAttempts": 5 } } }, { - "eventId": "50", - "eventTime": "2022-07-19T21:23:49.338021510Z", + "eventId": "60", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskStarted", - "version": "1011", - "taskId": "375403085", + "version": "0", + "taskId": "5243016", "activityTaskStartedEventAttributes": { - "scheduledEventId": "49", - "identity": "1@airbyte-worker-74797b7bc-9hwf7", - "requestId": "8e4da843-b5cb-42d3-8b23-7d9a1a50b255", - "attempt": 1, - "lastFailure": null + "scheduledEventId": "59", + "identity": "1@0a5c2d703445", + "requestId": "7fbea450-15f0-49a1-ad61-a701d428e657", + "attempt": 1 } }, { - "eventId": "51", - "eventTime": "2022-07-19T21:23:55.154819954Z", + "eventId": "61", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskCompleted", - "version": "1011", - "taskId": "375403086", + "version": "0", + "taskId": "5243017", "activityTaskCompletedEventAttributes": { "result": { "payloads": [ @@ -1026,24 +1260,24 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJzdGF0dXMiOiJzdWNjZWVkZWQifQ==" + "data": "ZmFsc2U=" } ] }, - "scheduledEventId": "49", - "startedEventId": "50", - "identity": "1@airbyte-worker-74797b7bc-9hwf7" + "scheduledEventId": "59", + "startedEventId": "60", + "identity": "1@0a5c2d703445" } }, { - "eventId": "52", - "eventTime": "2022-07-19T21:23:55.154826774Z", + "eventId": "62", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "375403087", + "version": "0", + "taskId": "5243018", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-gjw7f:0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -1051,40 +1285,106 @@ } }, { - "eventId": "53", - "eventTime": "2022-07-19T21:23:55.171573351Z", + "eventId": "63", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "375403091", + "version": "0", + "taskId": "5243022", "workflowTaskStartedEventAttributes": { - "scheduledEventId": "52", - "identity": "0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", - "requestId": "f98ff7fe-d08f-4b1e-9a4e-37452b4b7aa4" + "scheduledEventId": "62", + "identity": "1@0a5c2d703445", + "requestId": "20f0f19c-a272-40d6-820d-8cce6fb875f8" } }, { - "eventId": "54", - "eventTime": "2022-07-19T21:23:55.233171668Z", + "eventId": "64", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "375403095", + "version": "0", + "taskId": "5243025", "workflowTaskCompletedEventAttributes": { - "scheduledEventId": "52", - "startedEventId": "53", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", + "scheduledEventId": "62", + "startedEventId": "63", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { - "eventId": "55", - "eventTime": "2022-07-19T21:23:55.233209779Z", + "eventId": "65", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "MarkerRecorded", + "version": "0", + "taskId": "5243026", + "markerRecordedEventAttributes": { + "details": { + "version": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "MQ==" + } + ] + }, + "changeId": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "InRhc2tfcXVldWVfY2hhbmdlX2Zyb21fY29ubmVjdGlvbl91cGRhdGVyX3RvX3N5bmMi" + } + ] + } + }, + "markerName": "Version", + "workflowTaskCompletedEventId": "64" + } + }, + { + "eventId": "66", + "eventTime": "2023-01-05T22:40:08.000Z", + "eventType": "MarkerRecorded", + "version": "0", + "taskId": "5243027", + "markerRecordedEventAttributes": { + "details": { + "changeId": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "InJvdXRlX2FjdGl2aXR5Ig==" + } + ] + }, + "version": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "MQ==" + } + ] + } + }, + "markerName": "Version", + "workflowTaskCompletedEventId": "64" + } + }, + { + "eventId": "67", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskScheduled", - "version": "1011", - "taskId": "375403096", + "version": "0", + "taskId": "5243028", "activityTaskScheduledEventAttributes": { - "activityId": "0f4b6544-bdef-339d-befb-f56d7056b753", + "activityId": "85e794d8-ae5f-3678-a439-feeabdd2ff92", "activityType": { - "name": "Run" + "name": "Route" }, "namespace": "", "taskQueue": { @@ -1100,7 +1400,7 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJqb2JSdW5Db25maWciOnsiam9iSWQiOiIzNzk1NjciLCJhdHRlbXB0SWQiOjB9LCJsYXVuY2hlckNvbmZpZyI6eyJqb2JJZCI6IjM3OTU2NyIsImF0dGVtcHRJZCI6MCwiZG9ja2VySW1hZ2UiOiJhaXJieXRlL2Rlc3RpbmF0aW9uLWJpZ3F1ZXJ5OjEuMS4xMSJ9LCJjb25uZWN0aW9uQ29uZmlndXJhdGlvbiI6eyJjb25uZWN0aW9uQ29uZmlndXJhdGlvbiI6eyJkYXRhc2V0X2lkIjoiYWlyYnl0ZV9tYXN0ZXJfcmF3IiwicHJvamVjdF9pZCI6ImN1c3RvbWVycy1tYW5hZ2VkIiwibG9hZGluZ19tZXRob2QiOnsibWV0aG9kIjoiR0NTIFN0YWdpbmciLCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkhNQUNfS0VZIiwiaG1hY19rZXlfc2VjcmV0Ijp7Il9zZWNyZXQiOiJhaXJieXRlX3dvcmtzcGFjZV8zNDYwYjczYi1mMTIzLTQ1YmQtOTJjZC0xNGY1ZTMxYzQwY2Rfc2VjcmV0X2MyOWY4OTVhLTAzZDctNGYyOS04YzFlLThjYzliZmMwMDljYV92MSJ9LCJobWFjX2tleV9hY2Nlc3NfaWQiOnsiX3NlY3JldCI6ImFpcmJ5dGVfd29ya3NwYWNlXzM0NjBiNzNiLWYxMjMtNDViZC05MmNkLTE0ZjVlMzFjNDBjZF9zZWNyZXRfYjQ1NTM1MTItMzFkMC00YzhkLThhYTgtOWJlMjhlMjJjOWQwX3YxIn19LCJnY3NfYnVja2V0X25hbWUiOiJzbV9haXJieXRlX3N0YWdpbmdfc3luYyIsImdjc19idWNrZXRfcGF0aCI6InRtcF9pbmdlc3Rpb25fc3RhZ2luZyIsImtlZXBfZmlsZXNfaW5fZ2NzLWJ1Y2tldCI6IkRlbGV0ZSBhbGwgdG1wIGZpbGVzIGZyb20gR0NTIn0sImNyZWRlbnRpYWxzX2pzb24iOnsiX3NlY3JldCI6ImFpcmJ5dGVfd29ya3NwYWNlXzM0NjBiNzNiLWYxMjMtNDViZC05MmNkLTE0ZjVlMzFjNDBjZF9zZWNyZXRfODczYTFiZTQtOGVmYy00NzdiLWFhNTctMWJlOTZmNGYyZjcwX3YxIn0sImRhdGFzZXRfbG9jYXRpb24iOiJVUyIsInRyYW5zZm9ybWF0aW9uX3ByaW9yaXR5IjoiaW50ZXJhY3RpdmUiLCJiaWdfcXVlcnlfY2xpZW50X2J1ZmZlcl9zaXplX21iIjoxNX19fQ==" + "data": "eyJjb25uZWN0aW9uSWQiOiJiNjFkNWFjZC1iZjczLTRlZTctOGQ0Ni02OGY0MmQ5NTNmMjgifQ==" } ] }, @@ -1108,36 +1408,35 @@ "scheduleToStartTimeout": "0s", "startToCloseTimeout": "120s", "heartbeatTimeout": "30s", - "workflowTaskCompletedEventId": "54", + "workflowTaskCompletedEventId": "64", "retryPolicy": { + "nonRetryableErrorTypes": [], "initialInterval": "30s", "backoffCoefficient": 2, "maximumInterval": "600s", - "maximumAttempts": 5, - "nonRetryableErrorTypes": [] + "maximumAttempts": 5 } } }, { - "eventId": "56", - "eventTime": "2022-07-19T21:23:55.250817122Z", + "eventId": "68", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskStarted", - "version": "1011", - "taskId": "375403118", + "version": "0", + "taskId": "5243032", "activityTaskStartedEventAttributes": { - "scheduledEventId": "55", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", - "requestId": "d5982336-25f7-4186-8b5f-168a66d3e1e7", - "attempt": 1, - "lastFailure": null + "scheduledEventId": "67", + "identity": "1@0a5c2d703445", + "requestId": "1407437c-20bc-4c6d-99c5-2613704c6a5c", + "attempt": 1 } }, { - "eventId": "57", - "eventTime": "2022-07-19T21:24:06.498513155Z", + "eventId": "69", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ActivityTaskCompleted", - "version": "1011", - "taskId": "375403119", + "version": "0", + "taskId": "5243033", "activityTaskCompletedEventAttributes": { "result": { "payloads": [ @@ -1145,24 +1444,24 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJzdGF0dXMiOiJzdWNjZWVkZWQifQ==" + "data": "eyJ0YXNrUXVldWUiOiJTWU5DIn0=" } ] }, - "scheduledEventId": "55", - "startedEventId": "56", - "identity": "1@airbyte-worker-74797b7bc-gjw7f" + "scheduledEventId": "67", + "startedEventId": "68", + "identity": "1@0a5c2d703445" } }, { - "eventId": "58", - "eventTime": "2022-07-19T21:24:06.498520305Z", + "eventId": "70", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "375403120", + "version": "0", + "taskId": "5243034", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-gjw7f:0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -1170,74 +1469,39 @@ } }, { - "eventId": "59", - "eventTime": "2022-07-19T21:24:06.516712798Z", + "eventId": "71", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "375403124", + "version": "0", + "taskId": "5243038", "workflowTaskStartedEventAttributes": { - "scheduledEventId": "58", - "identity": "0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", - "requestId": "53d00fd0-3927-479b-9bd2-df595f19e07b" + "scheduledEventId": "70", + "identity": "1@0a5c2d703445", + "requestId": "b8788020-c653-4a9d-b0b4-95cab3faa94c" } }, { - "eventId": "60", - "eventTime": "2022-07-19T21:24:06.625287515Z", + "eventId": "72", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "375403128", + "version": "0", + "taskId": "5243041", "workflowTaskCompletedEventAttributes": { - "scheduledEventId": "58", - "startedEventId": "59", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", + "scheduledEventId": "70", + "startedEventId": "71", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { - "eventId": "61", - "eventTime": "2022-07-19T21:24:06.625312026Z", - "eventType": "MarkerRecorded", - "version": "1011", - "taskId": "375403129", - "markerRecordedEventAttributes": { - "markerName": "Version", - "details": { - "changeId": { - "payloads": [ - { - "metadata": { - "encoding": "anNvbi9wbGFpbg==" - }, - "data": "InRhc2tfcXVldWVfY2hhbmdlX2Zyb21fY29ubmVjdGlvbl91cGRhdGVyX3RvX3N5bmMi" - } - ] - }, - "version": { - "payloads": [ - { - "metadata": { - "encoding": "anNvbi9wbGFpbg==" - }, - "data": "MQ==" - } - ] - } - }, - "workflowTaskCompletedEventId": "60", - "header": null, - "failure": null - } - }, - { - "eventId": "62", - "eventTime": "2022-07-19T21:24:06.625337177Z", + "eventId": "73", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "StartChildWorkflowExecutionInitiated", - "version": "1011", - "taskId": "375403130", + "version": "0", + "taskId": "5243042", "startChildWorkflowExecutionInitiatedEventAttributes": { - "namespace": "prod.ebc2e", - "workflowId": "sync_379567", + "namespace": "default", + "workflowId": "sync_1", "workflowType": { "name": "SyncWorkflow" }, @@ -1251,62 +1515,59 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJqb2JJZCI6IjM3OTU2NyIsImF0dGVtcHRJZCI6MH0=" + "data": "eyJqb2JJZCI6IjEiLCJhdHRlbXB0SWQiOjB9" }, { "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJqb2JJZCI6IjM3OTU2NyIsImF0dGVtcHRJZCI6MCwiZG9ja2VySW1hZ2UiOiJhaXJieXRlL3NvdXJjZS1yZWNoYXJnZTowLjEuNSJ9" + "data": "eyJqb2JJZCI6IjEiLCJhdHRlbXB0SWQiOjAsImRvY2tlckltYWdlIjoiYWlyYnl0ZS9zb3VyY2UtcG9rZWFwaTowLjEuNSIsInN1cHBvcnRzRGJ0IjpmYWxzZSwicHJvdG9jb2xWZXJzaW9uIjp7InZlcnNpb24iOiIwLjIuMCJ9LCJpc0N1c3RvbUNvbm5lY3RvciI6ZmFsc2V9" }, { "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJqb2JJZCI6IjM3OTU2NyIsImF0dGVtcHRJZCI6MCwiZG9ja2VySW1hZ2UiOiJhaXJieXRlL2Rlc3RpbmF0aW9uLWJpZ3F1ZXJ5OjEuMS4xMSJ9" + "data": "eyJqb2JJZCI6IjEiLCJhdHRlbXB0SWQiOjAsImRvY2tlckltYWdlIjoiYWlyYnl0ZS9kZXN0aW5hdGlvbi1lMmUtdGVzdDowLjIuNCIsInN1cHBvcnRzRGJ0IjpmYWxzZSwicHJvdG9jb2xWZXJzaW9uIjp7InZlcnNpb24iOiIwLjIuMCJ9LCJpc0N1c3RvbUNvbm5lY3RvciI6ZmFsc2V9" }, { "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJuYW1lc3BhY2VEZWZpbml0aW9uIjoiZGVzdGluYXRpb24iLCJuYW1lc3BhY2VGb3JtYXQiOiIke1NPVVJDRV9OQU1FU1BBQ0V9IiwicHJlZml4IjoidGhldGlzdGVhX3JlY2hhcmdlXyIsInNvdXJjZUNvbmZpZ3VyYXRpb24iOnsic3RhcnRfZGF0ZSI6IjIwMTgtMTItMTNUMDA6MDA6MDBaIiwiYWNjZXNzX3Rva2VuIjp7Il9zZWNyZXQiOiJhaXJieXRlX3dvcmtzcGFjZV8zNDYwYjczYi1mMTIzLTQ1YmQtOTJjZC0xNGY1ZTMxYzQwY2Rfc2VjcmV0X2UzZDA1NDdlLWE3NWItNGIwNS1hMWE3LWIwNTg5ZjliNzQxNl92MSJ9fSwiZGVzdGluYXRpb25Db25maWd1cmF0aW9uIjp7ImRhdGFzZXRfaWQiOiJhaXJieXRlX21hc3Rlcl9yYXciLCJwcm9qZWN0X2lkIjoiY3VzdG9tZXJzLW1hbmFnZWQiLCJsb2FkaW5nX21ldGhvZCI6eyJtZXRob2QiOiJHQ1MgU3RhZ2luZyIsImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiSE1BQ19LRVkiLCJobWFjX2tleV9zZWNyZXQiOnsiX3NlY3JldCI6ImFpcmJ5dGVfd29ya3NwYWNlXzM0NjBiNzNiLWYxMjMtNDViZC05MmNkLTE0ZjVlMzFjNDBjZF9zZWNyZXRfYzI5Zjg5NWEtMDNkNy00ZjI5LThjMWUtOGNjOWJmYzAwOWNhX3YxIn0sImhtYWNfa2V5X2FjY2Vzc19pZCI6eyJfc2VjcmV0IjoiYWlyYnl0ZV93b3Jrc3BhY2VfMzQ2MGI3M2ItZjEyMy00NWJkLTkyY2QtMTRmNWUzMWM0MGNkX3NlY3JldF9iNDU1MzUxMi0zMWQwLTRjOGQtOGFhOC05YmUyOGUyMmM5ZDBfdjEifX0sImdjc19idWNrZXRfbmFtZSI6InNtX2FpcmJ5dGVfc3RhZ2luZ19zeW5jIiwiZ2NzX2J1Y2tldF9wYXRoIjoidG1wX2luZ2VzdGlvbl9zdGFnaW5nIiwia2VlcF9maWxlc19pbl9nY3MtYnVja2V0IjoiRGVsZXRlIGFsbCB0bXAgZmlsZXMgZnJvbSBHQ1MifSwiY3JlZGVudGlhbHNfanNvbiI6eyJfc2VjcmV0IjoiYWlyYnl0ZV93b3Jrc3BhY2VfMzQ2MGI3M2ItZjEyMy00NWJkLTkyY2QtMTRmNWUzMWM0MGNkX3NlY3JldF84NzNhMWJlNC04ZWZjLTQ3N2ItYWE1Ny0xYmU5NmY0ZjJmNzBfdjEifSwiZGF0YXNldF9sb2NhdGlvbiI6IlVTIiwidHJhbnNmb3JtYXRpb25fcHJpb3JpdHkiOiJpbnRlcmFjdGl2ZSIsImJpZ19xdWVyeV9jbGllbnRfYnVmZmVyX3NpemVfbWIiOjE1fSwib3BlcmF0aW9uU2VxdWVuY2UiOlt7Im9wZXJhdGlvbklkIjoiNWMxYjgwNWQtNzAzMC00NWIzLTliNjMtNDliZWExMDM1NTBkIiwibmFtZSI6Ik5vcm1hbGl6YXRpb24iLCJvcGVyYXRvclR5cGUiOiJub3JtYWxpemF0aW9uIiwib3BlcmF0b3JOb3JtYWxpemF0aW9uIjp7Im9wdGlvbiI6ImJhc2ljIn0sInRvbWJzdG9uZSI6ZmFsc2UsIndvcmtzcGFjZUlkIjoiMzQ2MGI3M2ItZjEyMy00NWJkLTkyY2QtMTRmNWUzMWM0MGNkIn1dLCJjYXRhbG9nIjp7InN0cmVhbXMiOlt7InN0cmVhbSI6eyJuYW1lIjoiY2hhcmdlcyIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJub3RlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGFncyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInR5cGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJlbWFpbCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImVycm9yIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic3RhdHVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGF4X2xpbmVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyIsIm51bWJlciJdfSwidG90YWxfdGF4Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyIsIm51bWJlciJdfSwiYWRkcmVzc19pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiZXJyb3JfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJsaW5lX2l0ZW1zIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJyZXRyeV9kYXRlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInRvdGFsX3ByaWNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicHJvY2Vzc2VkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwic2NoZWR1bGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidG90YWxfd2VpZ2h0Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImN1c3RvbWVyX2hhc2giOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0b3RhbF9yZWZ1bmRzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYW5hbHl0aWNzX2RhdGEiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJjbGllbnRfZGV0YWlscyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXX0sImRpc2NvdW50X2NvZGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOiJvYmplY3QifX0sInByb2Nlc3Nvcl9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hpcHBpbmdfbGluZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6Im9iamVjdCJ9fSwic3VidG90YWxfcHJpY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0cmFuc2FjdGlvbl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ6aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjaXR5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicGhvbmUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY291bnRyeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFkZHJlc3MxIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcm92aW5jZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNhcnRfbm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImRpc2NvdW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5vdGVfYXR0cmlidXRlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic2hpcHBpbmdfbGluZXNfb3ZlcnJpZGUiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX19fSwibm90ZV9hdHRyaWJ1dGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJzaGlwbWVudHNfY291bnQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwidG90YWxfZGlzY291bnRzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hpcHBpbmdfYWRkcmVzcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ6aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjaXR5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicGhvbmUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY291bnRyeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFkZHJlc3MxIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcm92aW5jZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNhcnRfbm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImRpc2NvdW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5vdGVfYXR0cmlidXRlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic2hpcHBpbmdfbGluZXNfb3ZlcnJpZGUiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX19fSwic2hvcGlmeV9vcmRlcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm51bWJlcl90aW1lc190cmllZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJoYXNfdW5jb21taXRlZF9jaGFuZ2VzIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sInRvdGFsX2xpbmVfaXRlbXNfcHJpY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJsYXN0X2NoYXJnZV9hdHRlbXB0X2RhdGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJzaG9waWZ5X3ZhcmlhbnRfaWRfbm90X2ZvdW5kIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiLCJzdHJpbmciXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoiY29sbGVjdGlvbnMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifX19LCJzdXBwb3J0ZWRfc3luY19tb2RlcyI6WyJmdWxsX3JlZnJlc2giXSwiZGVmYXVsdF9jdXJzb3JfZmllbGQiOltdLCJzb3VyY2VfZGVmaW5lZF9wcmltYXJ5X2tleSI6W1siaWQiXV19LCJzeW5jX21vZGUiOiJmdWxsX3JlZnJlc2giLCJjdXJzb3JfZmllbGQiOltdLCJkZXN0aW5hdGlvbl9zeW5jX21vZGUiOiJvdmVyd3JpdGUiLCJwcmltYXJ5X2tleSI6W1siaWQiXV19LHsic3RyZWFtIjp7Im5hbWUiOiJjdXN0b21lcnMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiaGFzaCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImVtYWlsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic3RhdHVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiYmlsbGluZ196aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2NpdHkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX3Bob25lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYW5hbHl0aWNzX2RhdGEiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJwcm9jZXNzb3JfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfY29tcGFueSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfY291bnRyeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzczEiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2FkZHJlc3MyIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19wcm92aW5jZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFjY2VwdHNfbWFya2V0aW5nIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImJpbGxpbmdfbGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19maXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hvcGlmeV9jdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm51bWJlcl9zdWJzY3JpcHRpb25zIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInN0cmlwZV9jdXN0b21lcl90b2tlbiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImhhc192YWxpZF9wYXltZW50X21ldGhvZCI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJmaXJzdF9jaGFyZ2VfcHJvY2Vzc2VkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiaGFzX2NhcmRfZXJyb3JfaW5fZHVubmluZyI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJudW1iZXJfYWN0aXZlX3N1YnNjcmlwdGlvbnMiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwicmVhc29uX3BheW1lbnRfbWV0aG9kX25vdF92YWxpZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoiZGlzY291bnRzIiwianNvbl9zY2hlbWEiOnsidHlwZSI6Im9iamVjdCIsIiRzY2hlbWEiOiJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImNvZGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ2YWx1ZSI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImVuZHNfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJkdXJhdGlvbiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInN0YXJ0c19hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImFwcGxpZXNfdG8iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidGltZXNfdXNlZCI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJ1c2FnZV9saW1pdCI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sImFwcGxpZXNfdG9faWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiZGlzY291bnRfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNoYW5uZWxfc2V0dGluZ3MiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsiYXBpIjp7InR5cGUiOiJvYmplY3QiLCJwcm9wZXJ0aWVzIjp7ImNhbl9hcHBseSI6eyJ0eXBlIjoiYm9vbGVhbiJ9fX0sImNoZWNrb3V0X3BhZ2UiOnsidHlwZSI6Im9iamVjdCIsInByb3BlcnRpZXMiOnsiY2FuX2FwcGx5Ijp7InR5cGUiOiJib29sZWFuIn19fSwiY3VzdG9tZXJfcG9ydGFsIjp7InR5cGUiOiJvYmplY3QiLCJwcm9wZXJ0aWVzIjp7ImNhbl9hcHBseSI6eyJ0eXBlIjoiYm9vbGVhbiJ9fX0sIm1lcmNoYW50X3BvcnRhbCI6eyJ0eXBlIjoib2JqZWN0IiwicHJvcGVydGllcyI6eyJjYW5fYXBwbHkiOnsidHlwZSI6ImJvb2xlYW4ifX19fX0sIm9uY2VfcGVyX2N1c3RvbWVyIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImFwcGxpZXNfdG9fcmVzb3VyY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJkdXJhdGlvbl91c2FnZV9saW1pdCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJleHRlcm5hbF9kaXNjb3VudF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFwcGxpZXNfdG9fcHJvZHVjdF90eXBlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZXh0ZXJuYWxfZGlzY291bnRfc291cmNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicHJlcmVxdWlzaXRlX3N1YnRvdGFsX21pbiI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sImZpcnN0X3RpbWVfY3VzdG9tZXJfcmVzdHJpY3Rpb24iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sInN1cHBvcnRlZF9zeW5jX21vZGVzIjpbImZ1bGxfcmVmcmVzaCIsImluY3JlbWVudGFsIl0sInNvdXJjZV9kZWZpbmVkX2N1cnNvciI6dHJ1ZSwiZGVmYXVsdF9jdXJzb3JfZmllbGQiOlsidXBkYXRlZF9hdCJdLCJzb3VyY2VfZGVmaW5lZF9wcmltYXJ5X2tleSI6W1siaWQiXV19LCJzeW5jX21vZGUiOiJpbmNyZW1lbnRhbCIsImN1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sImRlc3RpbmF0aW9uX3N5bmNfbW9kZSI6ImFwcGVuZF9kZWR1cCIsInByaW1hcnlfa2V5IjpbWyJpZCJdXX0seyJzdHJlYW0iOnsibmFtZSI6Im9uZXRpbWVzIiwianNvbl9zY2hlbWEiOnsidHlwZSI6Im9iamVjdCIsIiRzY2hlbWEiOiJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInNrdSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByaWNlIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiLCJzdHJpbmciLCJudW1iZXIiXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInF1YW50aXR5Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImFkZHJlc3NfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciIsInN0cmluZyJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInByb3BlcnRpZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJjdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIiwic3RyaW5nIl19LCJwcm9kdWN0X3RpdGxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidmFyaWFudF90aXRsZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInNob3BpZnlfcHJvZHVjdF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzaG9waWZ5X3ZhcmlhbnRfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwicmVjaGFyZ2VfcHJvZHVjdF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJuZXh0X2NoYXJnZV9zY2hlZHVsZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9fX0sInN1cHBvcnRlZF9zeW5jX21vZGVzIjpbImZ1bGxfcmVmcmVzaCIsImluY3JlbWVudGFsIl0sInNvdXJjZV9kZWZpbmVkX2N1cnNvciI6dHJ1ZSwiZGVmYXVsdF9jdXJzb3JfZmllbGQiOlsidXBkYXRlZF9hdCJdLCJzb3VyY2VfZGVmaW5lZF9wcmltYXJ5X2tleSI6W1siaWQiXV19LCJzeW5jX21vZGUiOiJpbmNyZW1lbnRhbCIsImN1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sImRlc3RpbmF0aW9uX3N5bmNfbW9kZSI6ImFwcGVuZF9kZWR1cCIsInByaW1hcnlfa2V5IjpbWyJpZCJdXX0seyJzdHJlYW0iOnsibmFtZSI6Im9yZGVycyIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJoYXNoIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInRhZ3MiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0eXBlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZW1haWwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzdGF0dXMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjdXJyZW5jeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImN1c3RvbWVyIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImhhc2giOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJlbWFpbCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImJpbGxpbmdfemlwIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19jaXR5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19waG9uZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByb2Nlc3Nvcl90eXBlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19jb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19jb3VudHJ5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19hZGRyZXNzMSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX3Byb3ZpbmNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19sYXN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2ZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaG9waWZ5X2N1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibnVtYmVyX3N1YnNjcmlwdGlvbnMiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiaGFzX3ZhbGlkX3BheW1lbnRfbWV0aG9kIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImZpcnN0X2NoYXJnZV9wcm9jZXNzZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJoYXNfY2FyZF9lcnJvcl9pbl9kdW5uaW5nIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sIm51bWJlcl9hY3RpdmVfc3Vic2NyaXB0aW9ucyI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJyZWFzb25fcGF5bWVudF9tZXRob2Rfbm90X3ZhbGlkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJyZWRhY3RlZCI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJjaGFyZ2VfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGF4X2xpbmVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJ0b3RhbF90YXgiOnsidHlwZSI6WyJudWxsIiwibnVtYmVyIiwic3RyaW5nIl19LCJhZGRyZXNzX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImJyb3dzZXJfaXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiZmlyc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImlzX3ByZXBhaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibGluZV9pdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjoib2JqZWN0IiwicHJvcGVydGllcyI6eyJza3UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJncmFtcyI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sInByaWNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGl0bGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJpbWFnZXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJxdWFudGl0eSI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJwcm9wZXJ0aWVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJ2YXJpYW50X3RpdGxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic3Vic2NyaXB0aW9uX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInNob3BpZnlfcHJvZHVjdF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciLCJpbnRlZ2VyIl19LCJzaG9waWZ5X3ZhcmlhbnRfaWQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIiwiaW50ZWdlciJdfSwiZXh0ZXJuYWxfaW52ZW50b3J5X3BvbGljeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJjdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ0b3RhbF9wcmljZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByb2Nlc3NlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInNjaGVkdWxlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInNoaXBwZWRfZGF0ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInRvdGFsX3dlaWdodCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJjaGFyZ2Vfc3RhdHVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidG90YWxfcmVmdW5kcyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImRpc2NvdW50X2NvZGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJzaGlwcGluZ19saW5lcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic3VidG90YWxfcHJpY2UiOnsidHlwZSI6WyJudWxsIiwibnVtYmVyIiwic3RyaW5nIl19LCJ0cmFuc2FjdGlvbl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ6aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjaXR5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicGhvbmUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY291bnRyeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFkZHJlc3MxIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcm92aW5jZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNhcnRfbm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImRpc2NvdW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5vdGVfYXR0cmlidXRlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic2hpcHBpbmdfbGluZXNfb3ZlcnJpZGUiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX19fSwibm90ZV9hdHRyaWJ1dGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJ0b3RhbF9kaXNjb3VudHMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaGlwcGluZ19hZGRyZXNzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInppcCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNpdHkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwaG9uZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNvbXBhbnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb3VudHJ5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczEiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJhZGRyZXNzMiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByb3ZpbmNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY2FydF9ub3RlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiY3VzdG9tZXJfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiZGlzY291bnRfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibm90ZV9hdHRyaWJ1dGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJzaGlwcGluZ19saW5lc19vdmVycmlkZSI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfX19LCJzaG9waWZ5X29yZGVyX2lkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzc19pc19hY3RpdmUiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwicGF5bWVudF9wcm9jZXNzb3IiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaG9waWZ5X2NhcnRfdG9rZW4iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaG9waWZ5X2N1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hvcGlmeV9vcmRlcl9udW1iZXIiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwidG90YWxfbGluZV9pdGVtc19wcmljZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoicHJvZHVjdHMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwidGl0bGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJoYW5kbGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJpbWFnZXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImNvbGxlY3Rpb25faWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiZGlzY291bnRfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImRpc2NvdW50X2Ftb3VudCI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sIm9yZGVyX2RheV9vZl93ZWVrIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImN1dG9mZl9kYXlfb2Zfd2VlayI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJvcmRlcl9kYXlfb2ZfbW9udGgiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic2hvcGlmeV9wcm9kdWN0X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImN1dG9mZl9kYXlfb2ZfbW9udGgiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwib3JkZXJfaW50ZXJ2YWxfdW5pdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm1vZGlmaWFibGVfcHJvcGVydGllcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic3Vic2NyaXB0aW9uX2RlZmF1bHRzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdfSwiY2hhcmdlX2ludGVydmFsX2ZyZXF1ZW5jeSI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzdG9yZWZyb250X3B1cmNoYXNlX29wdGlvbnMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJvcmRlcl9pbnRlcnZhbF9mcmVxdWVuY3lfb3B0aW9ucyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwiZXhwaXJlX2FmdGVyX3NwZWNpZmljX251bWJlcl9vZl9jaGFyZ2VzIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIl0sImRlZmF1bHRfY3Vyc29yX2ZpZWxkIjpbXSwic291cmNlX2RlZmluZWRfcHJpbWFyeV9rZXkiOltbImlkIl1dfSwic3luY19tb2RlIjoiZnVsbF9yZWZyZXNoIiwiY3Vyc29yX2ZpZWxkIjpbXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoib3ZlcndyaXRlIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoic2hvcCIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJzaG9wIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdfSwic3RvcmUiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19fX0sInN1cHBvcnRlZF9zeW5jX21vZGVzIjpbImZ1bGxfcmVmcmVzaCJdLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6W10sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJzaG9wIl0sWyJzdG9yZSJdXX0sInN5bmNfbW9kZSI6ImZ1bGxfcmVmcmVzaCIsImN1cnNvcl9maWVsZCI6W10sImRlc3RpbmF0aW9uX3N5bmNfbW9kZSI6Im92ZXJ3cml0ZSIsInByaW1hcnlfa2V5IjpbWyJzaG9wIl0sWyJzdG9yZSJdXX0seyJzdHJlYW0iOnsibmFtZSI6InN1YnNjcmlwdGlvbnMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic2t1Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZW1haWwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcmljZSI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInF1YW50aXR5Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImFkZHJlc3NfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImlzX3ByZXBhaWQiOnsidHlwZSI6WyJudWxsIiwiYm9vbGVhbiJdfSwicHJvcGVydGllcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjoib2JqZWN0IiwicHJvcGVydGllcyI6eyJuYW1lIjp7InR5cGUiOiJzdHJpbmcifSwidmFsdWUiOnsidHlwZSI6WyJzdHJpbmciLCJpbnRlZ2VyIl19fX19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiY3VzdG9tZXJfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiY2FuY2VsbGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiaXNfc2tpcHBhYmxlIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImlzX3N3YXBwYWJsZSI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJza3Vfb3ZlcnJpZGUiOnsidHlwZSI6WyJudWxsIiwiYm9vbGVhbiJdfSwiY29tbWl0X3VwZGF0ZSI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJwcm9kdWN0X3RpdGxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidmFyaWFudF90aXRsZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFuYWx5dGljc19kYXRhIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdfSwib3JkZXJfZGF5X29mX3dlZWsiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiaGFzX3F1ZXVlZF9jaGFyZ2VzIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm9yZGVyX2RheV9vZl9tb250aCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzaG9waWZ5X3Byb2R1Y3RfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic2hvcGlmeV92YXJpYW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImNhbmNlbGxhdGlvbl9yZWFzb24iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJtYXhfcmV0cmllc19yZWFjaGVkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm9yZGVyX2ludGVydmFsX3VuaXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJyZWNoYXJnZV9wcm9kdWN0X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5leHRfY2hhcmdlX3NjaGVkdWxlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm9yZGVyX2ludGVydmFsX2ZyZXF1ZW5jeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNoYXJnZV9pbnRlcnZhbF9mcmVxdWVuY3kiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjYW5jZWxsYXRpb25fcmVhc29uX2NvbW1lbnRzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZXhwaXJlX2FmdGVyX3NwZWNpZmljX251bWJlcl9vZl9jaGFyZ2VzIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfV19LCJzdGF0ZSI6eyJzdGF0ZSI6eyJvcmRlcnMiOnsidXBkYXRlZF9hdCI6IjIwMjItMDctMTlUMDA6MDg6MDgifSwiY2hhcmdlcyI6eyJ1cGRhdGVkX2F0IjoiMjAyMi0wNy0xOVQwMDowODowOSJ9LCJvbmV0aW1lcyI6eyJ1cGRhdGVkX2F0IjoiMjAyMi0wMy0yMlQxODoxMTozMCJ9LCJjdXN0b21lcnMiOnsidXBkYXRlZF9hdCI6IjIwMjItMDctMTlUMDA6MTI6MzIifSwiZGlzY291bnRzIjp7InVwZGF0ZWRfYXQiOiIyMDIyLTA3LTE4VDIyOjA3OjE0In0sInN1YnNjcmlwdGlvbnMiOnsidXBkYXRlZF9hdCI6IjIwMjItMDctMTlUMDA6MDg6MDcifX19LCJyZXNvdXJjZVJlcXVpcmVtZW50cyI6eyJjcHVfcmVxdWVzdCI6IjAuNSIsImNwdV9saW1pdCI6IjAuNSIsIm1lbW9yeV9yZXF1ZXN0IjoiNTAwTWkiLCJtZW1vcnlfbGltaXQiOiI1MDBNaSJ9LCJzb3VyY2VSZXNvdXJjZVJlcXVpcmVtZW50cyI6eyJjcHVfcmVxdWVzdCI6IjAuNSIsImNwdV9saW1pdCI6IjAuNSIsIm1lbW9yeV9yZXF1ZXN0IjoiNTAwTWkiLCJtZW1vcnlfbGltaXQiOiI1MDBNaSJ9LCJkZXN0aW5hdGlvblJlc291cmNlUmVxdWlyZW1lbnRzIjp7ImNwdV9yZXF1ZXN0IjoiMC41IiwiY3B1X2xpbWl0IjoiMC41IiwibWVtb3J5X3JlcXVlc3QiOiIxR2kiLCJtZW1vcnlfbGltaXQiOiIxR2kifX0=" + "data": "eyJuYW1lc3BhY2VEZWZpbml0aW9uIjoiZGVzdGluYXRpb24iLCJuYW1lc3BhY2VGb3JtYXQiOiIke1NPVVJDRV9OQU1FU1BBQ0V9IiwicHJlZml4IjoiIiwic291cmNlQ29uZmlndXJhdGlvbiI6eyJwb2tlbW9uX25hbWUiOiJkaXR0byJ9LCJkZXN0aW5hdGlvbkNvbmZpZ3VyYXRpb24iOnsidHlwZSI6IkxPR0dJTkciLCJsb2dnaW5nX2NvbmZpZyI6eyJsb2dnaW5nX3R5cGUiOiJGaXJzdE4iLCJtYXhfZW50cnlfY291bnQiOjEwMH19LCJvcGVyYXRpb25TZXF1ZW5jZSI6W10sImNhdGFsb2ciOnsic3RyZWFtcyI6W3sic3RyZWFtIjp7Im5hbWUiOiJwb2tlbW9uIiwianNvbl9zY2hlbWEiOnsidHlwZSI6Im9iamVjdCIsIiRzY2hlbWEiOiJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJmb3JtcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ1cmwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19fSwibW92ZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsibW92ZSI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ1cmwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJ2ZXJzaW9uX2dyb3VwX2RldGFpbHMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidmVyc2lvbl9ncm91cCI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ1cmwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJsZXZlbF9sZWFybmVkX2F0Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm1vdmVfbGVhcm5fbWV0aG9kIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX19fX19fX0sIm9yZGVyIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInN0YXRzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InN0YXQiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwiZWZmb3J0Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImJhc2Vfc3RhdCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19fX19LCJ0eXBlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJzbG90Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInR5cGUiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fX19fSwiaGVpZ2h0Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIndlaWdodCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzcGVjaWVzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sInNwcml0ZXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsiYmFja19zaGlueSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJhY2tfZmVtYWxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZnJvbnRfc2hpbnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiYWNrX2RlZmF1bHQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJmcm9udF9mZW1hbGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJmcm9udF9kZWZhdWx0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmFja19zaGlueV9mZW1hbGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJmcm9udF9zaGlueV9mZW1hbGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sImFiaWxpdGllcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJzbG90Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImFiaWxpdHkiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwiaXNfaGlkZGVuIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX19fX0sImhlbGRfaXRlbXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsiaXRlbSI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ1cmwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJ2ZXJzaW9uX2RldGFpbHMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsicmFyaXR5Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInZlcnNpb24iOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fX19fX19fSwiaXNfZGVmYXVsdCAiOnsidHlwZSI6WyJudWxsIiwiYm9vbGVhbiJdfSwiZ2FtZV9pbmRpY2VzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InZlcnNpb24iOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwiZ2FtZV9pbmRleCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19fX19LCJiYXNlX2V4cGVyaWVuY2UiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibG9jYXRpb25fYXJlYV9lbmNvdW50ZXJzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJzdXBwb3J0ZWRfc3luY19tb2RlcyI6WyJmdWxsX3JlZnJlc2giXSwiZGVmYXVsdF9jdXJzb3JfZmllbGQiOltdLCJzb3VyY2VfZGVmaW5lZF9wcmltYXJ5X2tleSI6W119LCJzeW5jX21vZGUiOiJmdWxsX3JlZnJlc2giLCJjdXJzb3JfZmllbGQiOltdLCJkZXN0aW5hdGlvbl9zeW5jX21vZGUiOiJvdmVyd3JpdGUiLCJwcmltYXJ5X2tleSI6W119XX0sInJlc291cmNlUmVxdWlyZW1lbnRzIjp7ImNwdV9yZXF1ZXN0IjoiIiwiY3B1X2xpbWl0IjoiIiwibWVtb3J5X3JlcXVlc3QiOiIiLCJtZW1vcnlfbGltaXQiOiIifSwic291cmNlUmVzb3VyY2VSZXF1aXJlbWVudHMiOnsiY3B1X3JlcXVlc3QiOiIiLCJjcHVfbGltaXQiOiIiLCJtZW1vcnlfcmVxdWVzdCI6IiIsIm1lbW9yeV9saW1pdCI6IiJ9LCJkZXN0aW5hdGlvblJlc291cmNlUmVxdWlyZW1lbnRzIjp7ImNwdV9yZXF1ZXN0IjoiIiwiY3B1X2xpbWl0IjoiIiwibWVtb3J5X3JlcXVlc3QiOiIiLCJtZW1vcnlfbGltaXQiOiIifSwid29ya3NwYWNlSWQiOiI4ZGY0MTdlNC02YTU0LTQxNzItYWU1MS1lMTI1NDA3ZGNiZWMifQ==" }, { "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "ImRmN2U2ODg2LTQ1ODEtNDBlZS1iYWU1LTllN2ZmMjcxZGEzYyI=" + "data": "ImI2MWQ1YWNkLWJmNzMtNGVlNy04ZDQ2LTY4ZjQyZDk1M2YyOCI=" } ] }, "workflowExecutionTimeout": "0s", "workflowRunTimeout": "0s", "workflowTaskTimeout": "10s", - "parentClosePolicy": "RequestCancel", + "parentClosePolicy": "PARENT_CLOSE_POLICY_REQUEST_CANCEL", "control": "", - "workflowTaskCompletedEventId": "60", - "workflowIdReusePolicy": "AllowDuplicate", - "retryPolicy": null, + "workflowTaskCompletedEventId": "72", + "workflowIdReusePolicy": "WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE", "cronSchedule": "", "header": { "fields": {} - }, - "memo": null, - "searchAttributes": null + } } }, { - "eventId": "63", - "eventTime": "2022-07-19T21:24:06.652196748Z", + "eventId": "74", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "ChildWorkflowExecutionStarted", - "version": "1011", - "taskId": "375403133", + "version": "0", + "taskId": "5243045", "childWorkflowExecutionStartedEventAttributes": { - "namespace": "prod.ebc2e", - "initiatedEventId": "62", + "namespace": "default", + "initiatedEventId": "73", "workflowExecution": { - "workflowId": "sync_379567", - "runId": "fbca2047-5b8a-4e1b-bd0e-ba9f1ab5b9bf" + "workflowId": "sync_1", + "runId": "75d8ee73-650b-4b5b-87dc-bfe1d5d9f9ce" }, "workflowType": { "name": "SyncWorkflow" @@ -1317,14 +1578,14 @@ } }, { - "eventId": "64", - "eventTime": "2022-07-19T21:24:06.652203738Z", + "eventId": "75", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "375403134", + "version": "0", + "taskId": "5243046", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-gjw7f:0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -1332,36 +1593,36 @@ } }, { - "eventId": "65", - "eventTime": "2022-07-19T21:24:06.668731291Z", + "eventId": "76", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "375403138", + "version": "0", + "taskId": "5243050", "workflowTaskStartedEventAttributes": { - "scheduledEventId": "64", - "identity": "0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", - "requestId": "3594b0fd-81e4-4613-9022-9bd12252d888" + "scheduledEventId": "75", + "identity": "1@0a5c2d703445", + "requestId": "e4b8bef3-f50c-4110-885e-2540c5dec129" } }, { - "eventId": "66", - "eventTime": "2022-07-19T21:24:06.774282229Z", + "eventId": "77", + "eventTime": "2023-01-05T22:40:08.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "375403142", + "version": "0", + "taskId": "5243053", "workflowTaskCompletedEventAttributes": { - "scheduledEventId": "64", - "startedEventId": "65", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", + "scheduledEventId": "75", + "startedEventId": "76", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { - "eventId": "67", - "eventTime": "2022-07-19T21:29:17.756160670Z", + "eventId": "78", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "ChildWorkflowExecutionCompleted", - "version": "1011", - "taskId": "375403217", + "version": "0", + "taskId": "5243055", "childWorkflowExecutionCompletedEventAttributes": { "result": { "payloads": [ @@ -1369,31 +1630,31 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJzdGFuZGFyZFN5bmNTdW1tYXJ5Ijp7InN0YXR1cyI6ImNvbXBsZXRlZCIsInJlY29yZHNTeW5jZWQiOjIyLCJieXRlc1N5bmNlZCI6MzI1MTIsInN0YXJ0VGltZSI6MTY1ODI2NTg2NjA5NiwiZW5kVGltZSI6MTY1ODI2NTk4MDIwMSwidG90YWxTdGF0cyI6eyJyZWNvcmRzRW1pdHRlZCI6MjIsImJ5dGVzRW1pdHRlZCI6MzI1MTIsInN0YXRlTWVzc2FnZXNFbWl0dGVkIjo2LCJyZWNvcmRzQ29tbWl0dGVkIjoyMn0sInN0cmVhbVN0YXRzIjpbeyJzdHJlYW1OYW1lIjoidGhldGlzdGVhX3JlY2hhcmdlX2NoYXJnZXMiLCJzdGF0cyI6eyJyZWNvcmRzRW1pdHRlZCI6NSwiYnl0ZXNFbWl0dGVkIjoxMjQ5NywicmVjb3Jkc0NvbW1pdHRlZCI6NX19LHsic3RyZWFtTmFtZSI6InRoZXRpc3RlYV9yZWNoYXJnZV9zaG9wIiwic3RhdHMiOnsicmVjb3Jkc0VtaXR0ZWQiOjEsImJ5dGVzRW1pdHRlZCI6MTQwMCwicmVjb3Jkc0NvbW1pdHRlZCI6MX19LHsic3RyZWFtTmFtZSI6InRoZXRpc3RlYV9yZWNoYXJnZV9wcm9kdWN0cyIsInN0YXRzIjp7InJlY29yZHNFbWl0dGVkIjo5LCJieXRlc0VtaXR0ZWQiOjEwODAyLCJyZWNvcmRzQ29tbWl0dGVkIjo5fX0seyJzdHJlYW1OYW1lIjoidGhldGlzdGVhX3JlY2hhcmdlX2N1c3RvbWVycyIsInN0YXRzIjp7InJlY29yZHNFbWl0dGVkIjoxLCJieXRlc0VtaXR0ZWQiOjc3NCwicmVjb3Jkc0NvbW1pdHRlZCI6MX19LHsic3RyZWFtTmFtZSI6InRoZXRpc3RlYV9yZWNoYXJnZV9vcmRlcnMiLCJzdGF0cyI6eyJyZWNvcmRzRW1pdHRlZCI6MSwiYnl0ZXNFbWl0dGVkIjoyOTU5LCJyZWNvcmRzQ29tbWl0dGVkIjoxfX0seyJzdHJlYW1OYW1lIjoidGhldGlzdGVhX3JlY2hhcmdlX29uZXRpbWVzIiwic3RhdHMiOnsicmVjb3Jkc0VtaXR0ZWQiOjEsImJ5dGVzRW1pdHRlZCI6NDM1LCJyZWNvcmRzQ29tbWl0dGVkIjoxfX0seyJzdHJlYW1OYW1lIjoidGhldGlzdGVhX3JlY2hhcmdlX3N1YnNjcmlwdGlvbnMiLCJzdGF0cyI6eyJyZWNvcmRzRW1pdHRlZCI6NCwiYnl0ZXNFbWl0dGVkIjozNjQ1LCJyZWNvcmRzQ29tbWl0dGVkIjo0fX1dfSwibm9ybWFsaXphdGlvblN1bW1hcnkiOnsic3RhcnRUaW1lIjoxNjU4MjY1OTk3MTg3LCJlbmRUaW1lIjoxNjU4MjY2MTU3MTg1fSwic3RhdGUiOnsic3RhdGUiOnsib3JkZXJzIjp7InVwZGF0ZWRfYXQiOiIyMDIyLTA3LTE5VDAwOjA4OjA4In0sImNoYXJnZXMiOnsidXBkYXRlZF9hdCI6IjIwMjItMDctMTlUMTY6MTM6MjQifSwib25ldGltZXMiOnsidXBkYXRlZF9hdCI6IjIwMjItMDMtMjJUMTg6MTE6MzAifSwiY3VzdG9tZXJzIjp7InVwZGF0ZWRfYXQiOiIyMDIyLTA3LTE5VDAwOjEyOjMyIn0sImRpc2NvdW50cyI6eyJ1cGRhdGVkX2F0IjoiMjAyMi0wNy0xOFQyMjowNzoxNCJ9LCJzdWJzY3JpcHRpb25zIjp7InVwZGF0ZWRfYXQiOiIyMDIyLTA3LTE5VDE2OjEzOjIyIn19fSwib3V0cHV0X2NhdGFsb2ciOnsic3RyZWFtcyI6W3sic3RyZWFtIjp7Im5hbWUiOiJ0aGV0aXN0ZWFfcmVjaGFyZ2VfY2hhcmdlcyIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJub3RlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGFncyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInR5cGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJlbWFpbCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImVycm9yIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic3RhdHVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGF4X2xpbmVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyIsIm51bWJlciJdfSwidG90YWxfdGF4Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyIsIm51bWJlciJdfSwiYWRkcmVzc19pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiZXJyb3JfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJsaW5lX2l0ZW1zIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJyZXRyeV9kYXRlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInRvdGFsX3ByaWNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicHJvY2Vzc2VkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwic2NoZWR1bGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidG90YWxfd2VpZ2h0Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImN1c3RvbWVyX2hhc2giOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0b3RhbF9yZWZ1bmRzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYW5hbHl0aWNzX2RhdGEiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJjbGllbnRfZGV0YWlscyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXX0sImRpc2NvdW50X2NvZGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOiJvYmplY3QifX0sInByb2Nlc3Nvcl9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hpcHBpbmdfbGluZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6Im9iamVjdCJ9fSwic3VidG90YWxfcHJpY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0cmFuc2FjdGlvbl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ6aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjaXR5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicGhvbmUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY291bnRyeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFkZHJlc3MxIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcm92aW5jZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNhcnRfbm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImRpc2NvdW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5vdGVfYXR0cmlidXRlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic2hpcHBpbmdfbGluZXNfb3ZlcnJpZGUiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX19fSwibm90ZV9hdHRyaWJ1dGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJzaGlwbWVudHNfY291bnQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwidG90YWxfZGlzY291bnRzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hpcHBpbmdfYWRkcmVzcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ6aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjaXR5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicGhvbmUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY291bnRyeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFkZHJlc3MxIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcm92aW5jZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNhcnRfbm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImRpc2NvdW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5vdGVfYXR0cmlidXRlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic2hpcHBpbmdfbGluZXNfb3ZlcnJpZGUiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX19fSwic2hvcGlmeV9vcmRlcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm51bWJlcl90aW1lc190cmllZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJoYXNfdW5jb21taXRlZF9jaGFuZ2VzIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sInRvdGFsX2xpbmVfaXRlbXNfcHJpY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJsYXN0X2NoYXJnZV9hdHRlbXB0X2RhdGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJzaG9waWZ5X3ZhcmlhbnRfaWRfbm90X2ZvdW5kIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiLCJzdHJpbmciXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoidGhldGlzdGVhX3JlY2hhcmdlX2NvbGxlY3Rpb25zIiwianNvbl9zY2hlbWEiOnsidHlwZSI6Im9iamVjdCIsIiRzY2hlbWEiOiJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIl0sImRlZmF1bHRfY3Vyc29yX2ZpZWxkIjpbXSwic291cmNlX2RlZmluZWRfcHJpbWFyeV9rZXkiOltbImlkIl1dfSwic3luY19tb2RlIjoiZnVsbF9yZWZyZXNoIiwiY3Vyc29yX2ZpZWxkIjpbXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoib3ZlcndyaXRlIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoidGhldGlzdGVhX3JlY2hhcmdlX2N1c3RvbWVycyIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJoYXNoIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZW1haWwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzdGF0dXMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJsYXN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiZmlyc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJiaWxsaW5nX3ppcCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfY2l0eSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfcGhvbmUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJhbmFseXRpY3NfZGF0YSI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXX0sInByb2Nlc3Nvcl90eXBlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19jb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19jb3VudHJ5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19hZGRyZXNzMSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX3Byb3ZpbmNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWNjZXB0c19tYXJrZXRpbmciOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiYmlsbGluZ19sYXN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2ZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaG9waWZ5X2N1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibnVtYmVyX3N1YnNjcmlwdGlvbnMiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic3RyaXBlX2N1c3RvbWVyX3Rva2VuIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiaGFzX3ZhbGlkX3BheW1lbnRfbWV0aG9kIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImZpcnN0X2NoYXJnZV9wcm9jZXNzZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJoYXNfY2FyZF9lcnJvcl9pbl9kdW5uaW5nIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sIm51bWJlcl9hY3RpdmVfc3Vic2NyaXB0aW9ucyI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJyZWFzb25fcGF5bWVudF9tZXRob2Rfbm90X3ZhbGlkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJzdXBwb3J0ZWRfc3luY19tb2RlcyI6WyJmdWxsX3JlZnJlc2giLCJpbmNyZW1lbnRhbCJdLCJzb3VyY2VfZGVmaW5lZF9jdXJzb3IiOnRydWUsImRlZmF1bHRfY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwic291cmNlX2RlZmluZWRfcHJpbWFyeV9rZXkiOltbImlkIl1dfSwic3luY19tb2RlIjoiaW5jcmVtZW50YWwiLCJjdXJzb3JfZmllbGQiOlsidXBkYXRlZF9hdCJdLCJkZXN0aW5hdGlvbl9zeW5jX21vZGUiOiJhcHBlbmRfZGVkdXAiLCJwcmltYXJ5X2tleSI6W1siaWQiXV19LHsic3RyZWFtIjp7Im5hbWUiOiJ0aGV0aXN0ZWFfcmVjaGFyZ2VfZGlzY291bnRzIiwianNvbl9zY2hlbWEiOnsidHlwZSI6Im9iamVjdCIsIiRzY2hlbWEiOiJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImNvZGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ2YWx1ZSI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImVuZHNfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJkdXJhdGlvbiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInN0YXJ0c19hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImFwcGxpZXNfdG8iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidGltZXNfdXNlZCI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJ1c2FnZV9saW1pdCI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sImFwcGxpZXNfdG9faWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiZGlzY291bnRfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNoYW5uZWxfc2V0dGluZ3MiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsiYXBpIjp7InR5cGUiOiJvYmplY3QiLCJwcm9wZXJ0aWVzIjp7ImNhbl9hcHBseSI6eyJ0eXBlIjoiYm9vbGVhbiJ9fX0sImNoZWNrb3V0X3BhZ2UiOnsidHlwZSI6Im9iamVjdCIsInByb3BlcnRpZXMiOnsiY2FuX2FwcGx5Ijp7InR5cGUiOiJib29sZWFuIn19fSwiY3VzdG9tZXJfcG9ydGFsIjp7InR5cGUiOiJvYmplY3QiLCJwcm9wZXJ0aWVzIjp7ImNhbl9hcHBseSI6eyJ0eXBlIjoiYm9vbGVhbiJ9fX0sIm1lcmNoYW50X3BvcnRhbCI6eyJ0eXBlIjoib2JqZWN0IiwicHJvcGVydGllcyI6eyJjYW5fYXBwbHkiOnsidHlwZSI6ImJvb2xlYW4ifX19fX0sIm9uY2VfcGVyX2N1c3RvbWVyIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImFwcGxpZXNfdG9fcmVzb3VyY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJkdXJhdGlvbl91c2FnZV9saW1pdCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJleHRlcm5hbF9kaXNjb3VudF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFwcGxpZXNfdG9fcHJvZHVjdF90eXBlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZXh0ZXJuYWxfZGlzY291bnRfc291cmNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicHJlcmVxdWlzaXRlX3N1YnRvdGFsX21pbiI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sImZpcnN0X3RpbWVfY3VzdG9tZXJfcmVzdHJpY3Rpb24iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sInN1cHBvcnRlZF9zeW5jX21vZGVzIjpbImZ1bGxfcmVmcmVzaCIsImluY3JlbWVudGFsIl0sInNvdXJjZV9kZWZpbmVkX2N1cnNvciI6dHJ1ZSwiZGVmYXVsdF9jdXJzb3JfZmllbGQiOlsidXBkYXRlZF9hdCJdLCJzb3VyY2VfZGVmaW5lZF9wcmltYXJ5X2tleSI6W1siaWQiXV19LCJzeW5jX21vZGUiOiJpbmNyZW1lbnRhbCIsImN1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sImRlc3RpbmF0aW9uX3N5bmNfbW9kZSI6ImFwcGVuZF9kZWR1cCIsInByaW1hcnlfa2V5IjpbWyJpZCJdXX0seyJzdHJlYW0iOnsibmFtZSI6InRoZXRpc3RlYV9yZWNoYXJnZV9vbmV0aW1lcyIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJza3UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcmljZSI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIiwic3RyaW5nIiwibnVtYmVyIl19LCJzdGF0dXMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJxdWFudGl0eSI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJhZGRyZXNzX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJwcm9wZXJ0aWVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiY3VzdG9tZXJfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciIsInN0cmluZyJdfSwicHJvZHVjdF90aXRsZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInZhcmlhbnRfdGl0bGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaG9waWZ5X3Byb2R1Y3RfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic2hvcGlmeV92YXJpYW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInJlY2hhcmdlX3Byb2R1Y3RfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibmV4dF9jaGFyZ2Vfc2NoZWR1bGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifX19LCJzdXBwb3J0ZWRfc3luY19tb2RlcyI6WyJmdWxsX3JlZnJlc2giLCJpbmNyZW1lbnRhbCJdLCJzb3VyY2VfZGVmaW5lZF9jdXJzb3IiOnRydWUsImRlZmF1bHRfY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwic291cmNlX2RlZmluZWRfcHJpbWFyeV9rZXkiOltbImlkIl1dfSwic3luY19tb2RlIjoiaW5jcmVtZW50YWwiLCJjdXJzb3JfZmllbGQiOlsidXBkYXRlZF9hdCJdLCJkZXN0aW5hdGlvbl9zeW5jX21vZGUiOiJhcHBlbmRfZGVkdXAiLCJwcmltYXJ5X2tleSI6W1siaWQiXV19LHsic3RyZWFtIjp7Im5hbWUiOiJ0aGV0aXN0ZWFfcmVjaGFyZ2Vfb3JkZXJzIiwianNvbl9zY2hlbWEiOnsidHlwZSI6Im9iamVjdCIsIiRzY2hlbWEiOiJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImhhc2giOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJub3RlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGFncyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInR5cGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJlbWFpbCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImN1cnJlbmN5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY3VzdG9tZXIiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiaGFzaCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImVtYWlsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic3RhdHVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiYmlsbGluZ196aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2NpdHkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX3Bob25lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicHJvY2Vzc29yX3R5cGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2NvbXBhbnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2NvdW50cnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2FkZHJlc3MxIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19hZGRyZXNzMiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfcHJvdmluY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2xhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfZmlyc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInNob3BpZnlfY3VzdG9tZXJfaWQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJudW1iZXJfc3Vic2NyaXB0aW9ucyI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJoYXNfdmFsaWRfcGF5bWVudF9tZXRob2QiOnsidHlwZSI6WyJudWxsIiwiYm9vbGVhbiJdfSwiZmlyc3RfY2hhcmdlX3Byb2Nlc3NlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImhhc19jYXJkX2Vycm9yX2luX2R1bm5pbmciOnsidHlwZSI6WyJudWxsIiwiYm9vbGVhbiJdfSwibnVtYmVyX2FjdGl2ZV9zdWJzY3JpcHRpb25zIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInJlYXNvbl9wYXltZW50X21ldGhvZF9ub3RfdmFsaWQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sInJlZGFjdGVkIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImNoYXJnZV9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJsYXN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0YXhfbGluZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX0sInRvdGFsX3RheCI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiLCJzdHJpbmciXX0sImFkZHJlc3NfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiYnJvd3Nlcl9pcCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiaXNfcHJlcGFpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJsaW5lX2l0ZW1zIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOiJvYmplY3QiLCJwcm9wZXJ0aWVzIjp7InNrdSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImdyYW1zIjp7InR5cGUiOlsibnVsbCIsIm51bWJlciJdfSwicHJpY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0aXRsZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImltYWdlcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXX0sInF1YW50aXR5Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInByb3BlcnRpZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX0sInZhcmlhbnRfdGl0bGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzdWJzY3JpcHRpb25faWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic2hvcGlmeV9wcm9kdWN0X2lkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyIsImludGVnZXIiXX0sInNob3BpZnlfdmFyaWFudF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciLCJpbnRlZ2VyIl19LCJleHRlcm5hbF9pbnZlbnRvcnlfcG9saWN5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19fSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInRvdGFsX3ByaWNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicHJvY2Vzc2VkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwic2NoZWR1bGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwic2hpcHBlZF9kYXRlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidG90YWxfd2VpZ2h0Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImNoYXJnZV9zdGF0dXMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0b3RhbF9yZWZ1bmRzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZGlzY291bnRfY29kZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX0sInNoaXBwaW5nX2xpbmVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJzdWJ0b3RhbF9wcmljZSI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiLCJzdHJpbmciXX0sInRyYW5zYWN0aW9uX2lkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19hZGRyZXNzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInppcCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNpdHkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwaG9uZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNvbXBhbnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb3VudHJ5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczEiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJhZGRyZXNzMiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByb3ZpbmNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY2FydF9ub3RlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiY3VzdG9tZXJfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiZGlzY291bnRfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibm90ZV9hdHRyaWJ1dGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJzaGlwcGluZ19saW5lc19vdmVycmlkZSI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfX19LCJub3RlX2F0dHJpYnV0ZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX0sInRvdGFsX2Rpc2NvdW50cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInNoaXBwaW5nX2FkZHJlc3MiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiemlwIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY2l0eSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInBob25lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY29tcGFueSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNvdW50cnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJhZGRyZXNzMSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFkZHJlc3MyIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicHJvdmluY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjYXJ0X25vdGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJsYXN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiZmlyc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJjdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJkaXNjb3VudF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJub3RlX2F0dHJpYnV0ZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX0sInNoaXBwaW5nX2xpbmVzX292ZXJyaWRlIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19fX0sInNob3BpZnlfb3JkZXJfaWQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJhZGRyZXNzX2lzX2FjdGl2ZSI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJwYXltZW50X3Byb2Nlc3NvciI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInNob3BpZnlfY2FydF90b2tlbiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInNob3BpZnlfY3VzdG9tZXJfaWQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaG9waWZ5X29yZGVyX251bWJlciI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ0b3RhbF9saW5lX2l0ZW1zX3ByaWNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJzdXBwb3J0ZWRfc3luY19tb2RlcyI6WyJmdWxsX3JlZnJlc2giLCJpbmNyZW1lbnRhbCJdLCJzb3VyY2VfZGVmaW5lZF9jdXJzb3IiOnRydWUsImRlZmF1bHRfY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwic291cmNlX2RlZmluZWRfcHJpbWFyeV9rZXkiOltbImlkIl1dfSwic3luY19tb2RlIjoiaW5jcmVtZW50YWwiLCJjdXJzb3JfZmllbGQiOlsidXBkYXRlZF9hdCJdLCJkZXN0aW5hdGlvbl9zeW5jX21vZGUiOiJhcHBlbmRfZGVkdXAiLCJwcmltYXJ5X2tleSI6W1siaWQiXV19LHsic3RyZWFtIjp7Im5hbWUiOiJ0aGV0aXN0ZWFfcmVjaGFyZ2VfcHJvZHVjdHMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwidGl0bGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJoYW5kbGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJpbWFnZXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImNvbGxlY3Rpb25faWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiZGlzY291bnRfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImRpc2NvdW50X2Ftb3VudCI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sIm9yZGVyX2RheV9vZl93ZWVrIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImN1dG9mZl9kYXlfb2Zfd2VlayI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJvcmRlcl9kYXlfb2ZfbW9udGgiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic2hvcGlmeV9wcm9kdWN0X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImN1dG9mZl9kYXlfb2ZfbW9udGgiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwib3JkZXJfaW50ZXJ2YWxfdW5pdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm1vZGlmaWFibGVfcHJvcGVydGllcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic3Vic2NyaXB0aW9uX2RlZmF1bHRzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdfSwiY2hhcmdlX2ludGVydmFsX2ZyZXF1ZW5jeSI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzdG9yZWZyb250X3B1cmNoYXNlX29wdGlvbnMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJvcmRlcl9pbnRlcnZhbF9mcmVxdWVuY3lfb3B0aW9ucyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwiZXhwaXJlX2FmdGVyX3NwZWNpZmljX251bWJlcl9vZl9jaGFyZ2VzIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIl0sImRlZmF1bHRfY3Vyc29yX2ZpZWxkIjpbXSwic291cmNlX2RlZmluZWRfcHJpbWFyeV9rZXkiOltbImlkIl1dfSwic3luY19tb2RlIjoiZnVsbF9yZWZyZXNoIiwiY3Vyc29yX2ZpZWxkIjpbXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoib3ZlcndyaXRlIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoidGhldGlzdGVhX3JlY2hhcmdlX3Nob3AiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsic2hvcCI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXX0sInN0b3JlIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdfX19LCJzdXBwb3J0ZWRfc3luY19tb2RlcyI6WyJmdWxsX3JlZnJlc2giXSwiZGVmYXVsdF9jdXJzb3JfZmllbGQiOltdLCJzb3VyY2VfZGVmaW5lZF9wcmltYXJ5X2tleSI6W1sic2hvcCJdLFsic3RvcmUiXV19LCJzeW5jX21vZGUiOiJmdWxsX3JlZnJlc2giLCJjdXJzb3JfZmllbGQiOltdLCJkZXN0aW5hdGlvbl9zeW5jX21vZGUiOiJvdmVyd3JpdGUiLCJwcmltYXJ5X2tleSI6W1sic2hvcCJdLFsic3RvcmUiXV19LHsic3RyZWFtIjp7Im5hbWUiOiJ0aGV0aXN0ZWFfcmVjaGFyZ2Vfc3Vic2NyaXB0aW9ucyIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJza3UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJlbWFpbCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByaWNlIjp7InR5cGUiOlsibnVsbCIsIm51bWJlciJdfSwic3RhdHVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicXVhbnRpdHkiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiYWRkcmVzc19pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiaXNfcHJlcGFpZCI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJwcm9wZXJ0aWVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOiJvYmplY3QiLCJwcm9wZXJ0aWVzIjp7Im5hbWUiOnsidHlwZSI6InN0cmluZyJ9LCJ2YWx1ZSI6eyJ0eXBlIjpbInN0cmluZyIsImludGVnZXIiXX19fX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJjdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJjYW5jZWxsZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJpc19za2lwcGFibGUiOnsidHlwZSI6WyJudWxsIiwiYm9vbGVhbiJdfSwiaXNfc3dhcHBhYmxlIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sInNrdV9vdmVycmlkZSI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJjb21taXRfdXBkYXRlIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sInByb2R1Y3RfdGl0bGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ2YXJpYW50X3RpdGxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYW5hbHl0aWNzX2RhdGEiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJvcmRlcl9kYXlfb2Zfd2VlayI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJoYXNfcXVldWVkX2NoYXJnZXMiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwib3JkZXJfZGF5X29mX21vbnRoIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInNob3BpZnlfcHJvZHVjdF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzaG9waWZ5X3ZhcmlhbnRfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiY2FuY2VsbGF0aW9uX3JlYXNvbiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm1heF9yZXRyaWVzX3JlYWNoZWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwib3JkZXJfaW50ZXJ2YWxfdW5pdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInJlY2hhcmdlX3Byb2R1Y3RfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibmV4dF9jaGFyZ2Vfc2NoZWR1bGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwib3JkZXJfaW50ZXJ2YWxfZnJlcXVlbmN5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY2hhcmdlX2ludGVydmFsX2ZyZXF1ZW5jeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNhbmNlbGxhdGlvbl9yZWFzb25fY29tbWVudHMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJleHBpcmVfYWZ0ZXJfc3BlY2lmaWNfbnVtYmVyX29mX2NoYXJnZXMiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfX19LCJzdXBwb3J0ZWRfc3luY19tb2RlcyI6WyJmdWxsX3JlZnJlc2giLCJpbmNyZW1lbnRhbCJdLCJzb3VyY2VfZGVmaW5lZF9jdXJzb3IiOnRydWUsImRlZmF1bHRfY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwic291cmNlX2RlZmluZWRfcHJpbWFyeV9rZXkiOltbImlkIl1dfSwic3luY19tb2RlIjoiaW5jcmVtZW50YWwiLCJjdXJzb3JfZmllbGQiOlsidXBkYXRlZF9hdCJdLCJkZXN0aW5hdGlvbl9zeW5jX21vZGUiOiJhcHBlbmRfZGVkdXAiLCJwcmltYXJ5X2tleSI6W1siaWQiXV19XX0sImZhaWx1cmVzIjpbXX0=" + "data": "eyJzdGFuZGFyZFN5bmNTdW1tYXJ5Ijp7InN0YXR1cyI6ImNvbXBsZXRlZCIsInJlY29yZHNTeW5jZWQiOjEsImJ5dGVzU3luY2VkIjoyMjcxNSwic3RhcnRUaW1lIjoxNjcyOTU4NDA4NjY1LCJlbmRUaW1lIjoxNjcyOTU4NDExODEzLCJ0b3RhbFN0YXRzIjp7ImJ5dGVzRW1pdHRlZCI6MjI3MTUsImRlc3RpbmF0aW9uU3RhdGVNZXNzYWdlc0VtaXR0ZWQiOjAsImRlc3RpbmF0aW9uV3JpdGVFbmRUaW1lIjoxNjcyOTU4NDExODExLCJkZXN0aW5hdGlvbldyaXRlU3RhcnRUaW1lIjoxNjcyOTU4NDA4NzM2LCJtZWFuU2Vjb25kc0JlZm9yZVNvdXJjZVN0YXRlTWVzc2FnZUVtaXR0ZWQiOjAsIm1heFNlY29uZHNCZWZvcmVTb3VyY2VTdGF0ZU1lc3NhZ2VFbWl0dGVkIjowLCJtYXhTZWNvbmRzQmV0d2VlblN0YXRlTWVzc2FnZUVtaXR0ZWRhbmRDb21taXR0ZWQiOjAsIm1lYW5TZWNvbmRzQmV0d2VlblN0YXRlTWVzc2FnZUVtaXR0ZWRhbmRDb21taXR0ZWQiOjAsInJlY29yZHNFbWl0dGVkIjoxLCJyZWNvcmRzQ29tbWl0dGVkIjoxLCJyZXBsaWNhdGlvbkVuZFRpbWUiOjE2NzI5NTg0MTE4MTIsInJlcGxpY2F0aW9uU3RhcnRUaW1lIjoxNjcyOTU4NDA4NjY1LCJzb3VyY2VSZWFkRW5kVGltZSI6MTY3Mjk1ODQxMTcwMCwic291cmNlUmVhZFN0YXJ0VGltZSI6MTY3Mjk1ODQwODcwMCwic291cmNlU3RhdGVNZXNzYWdlc0VtaXR0ZWQiOjB9LCJzdHJlYW1TdGF0cyI6W3sic3RyZWFtTmFtZSI6InBva2Vtb24iLCJzdGF0cyI6eyJieXRlc0VtaXR0ZWQiOjIyNzE1LCJyZWNvcmRzRW1pdHRlZCI6MSwicmVjb3Jkc0NvbW1pdHRlZCI6MX19XX0sIm91dHB1dF9jYXRhbG9nIjp7InN0cmVhbXMiOlt7InN0cmVhbSI6eyJuYW1lIjoicG9rZW1vbiIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZm9ybXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fX0sIm1vdmVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7Im1vdmUiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwidmVyc2lvbl9ncm91cF9kZXRhaWxzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InZlcnNpb25fZ3JvdXAiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwibGV2ZWxfbGVhcm5lZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJtb3ZlX2xlYXJuX21ldGhvZCI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ1cmwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19fX19fX19LCJvcmRlciI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzdGF0cyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJzdGF0Ijp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sImVmZm9ydCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJiYXNlX3N0YXQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfX19fSwidHlwZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsic2xvdCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ0eXBlIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX19fX0sImhlaWdodCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ3ZWlnaHQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic3BlY2llcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ1cmwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJzcHJpdGVzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7ImJhY2tfc2hpbnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiYWNrX2ZlbWFsZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImZyb250X3NoaW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmFja19kZWZhdWx0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZnJvbnRfZmVtYWxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZnJvbnRfZGVmYXVsdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJhY2tfc2hpbnlfZmVtYWxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZnJvbnRfc2hpbnlfZmVtYWxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJhYmlsaXRpZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsic2xvdCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJhYmlsaXR5Ijp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sImlzX2hpZGRlbiI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19fX19LCJoZWxkX2l0ZW1zIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7Iml0ZW0iOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwidmVyc2lvbl9kZXRhaWxzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InJhcml0eSI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ2ZXJzaW9uIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX19fX19fX0sImlzX2RlZmF1bHQgIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImdhbWVfaW5kaWNlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ2ZXJzaW9uIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sImdhbWVfaW5kZXgiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfX19fSwiYmFzZV9leHBlcmllbmNlIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImxvY2F0aW9uX2FyZWFfZW5jb3VudGVycyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIl0sImRlZmF1bHRfY3Vyc29yX2ZpZWxkIjpbXSwic291cmNlX2RlZmluZWRfcHJpbWFyeV9rZXkiOltdfSwic3luY19tb2RlIjoiZnVsbF9yZWZyZXNoIiwiY3Vyc29yX2ZpZWxkIjpbXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoib3ZlcndyaXRlIiwicHJpbWFyeV9rZXkiOltdfV19LCJmYWlsdXJlcyI6W119" } ] }, - "namespace": "prod.ebc2e", + "namespace": "default", "workflowExecution": { - "workflowId": "sync_379567", - "runId": "fbca2047-5b8a-4e1b-bd0e-ba9f1ab5b9bf" + "workflowId": "sync_1", + "runId": "75d8ee73-650b-4b5b-87dc-bfe1d5d9f9ce" }, "workflowType": { "name": "SyncWorkflow" }, - "initiatedEventId": "62", - "startedEventId": "63" + "initiatedEventId": "73", + "startedEventId": "74" } }, { - "eventId": "68", - "eventTime": "2022-07-19T21:29:17.756168051Z", + "eventId": "79", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "375403218", + "version": "0", + "taskId": "5243056", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-gjw7f:0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -1401,38 +1662,38 @@ } }, { - "eventId": "69", - "eventTime": "2022-07-19T21:29:17.771703275Z", + "eventId": "80", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "375403222", + "version": "0", + "taskId": "5243060", "workflowTaskStartedEventAttributes": { - "scheduledEventId": "68", - "identity": "0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", - "requestId": "4e4e6ad4-6632-4200-8cf4-e120a92d63c8" + "scheduledEventId": "79", + "identity": "1@0a5c2d703445", + "requestId": "5ec6899b-f0fa-4c29-b690-23e57887eba8" } }, { - "eventId": "70", - "eventTime": "2022-07-19T21:29:17.921390325Z", + "eventId": "81", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "375403226", + "version": "0", + "taskId": "5243063", "workflowTaskCompletedEventAttributes": { - "scheduledEventId": "68", - "startedEventId": "69", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", + "scheduledEventId": "79", + "startedEventId": "80", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { - "eventId": "71", - "eventTime": "2022-07-19T21:29:17.921421456Z", + "eventId": "82", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "ActivityTaskScheduled", - "version": "1011", - "taskId": "375403227", + "version": "0", + "taskId": "5243064", "activityTaskScheduledEventAttributes": { - "activityId": "683ce5db-52d5-381c-87de-5543d68ee104", + "activityId": "2c35a7da-9e07-37d1-bad3-39ea038e21d0", "activityType": { "name": "JobSuccessWithAttemptNumber" }, @@ -1450,7 +1711,7 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJqb2JJZCI6Mzc5NTY3LCJhdHRlbXB0TnVtYmVyIjowLCJzdGFuZGFyZFN5bmNPdXRwdXQiOnsic3RhbmRhcmRTeW5jU3VtbWFyeSI6eyJzdGF0dXMiOiJjb21wbGV0ZWQiLCJyZWNvcmRzU3luY2VkIjoyMiwiYnl0ZXNTeW5jZWQiOjMyNTEyLCJzdGFydFRpbWUiOjE2NTgyNjU4NjYwOTYsImVuZFRpbWUiOjE2NTgyNjU5ODAyMDEsInRvdGFsU3RhdHMiOnsicmVjb3Jkc0VtaXR0ZWQiOjIyLCJieXRlc0VtaXR0ZWQiOjMyNTEyLCJzdGF0ZU1lc3NhZ2VzRW1pdHRlZCI6NiwicmVjb3Jkc0NvbW1pdHRlZCI6MjJ9LCJzdHJlYW1TdGF0cyI6W3sic3RyZWFtTmFtZSI6InRoZXRpc3RlYV9yZWNoYXJnZV9jaGFyZ2VzIiwic3RhdHMiOnsicmVjb3Jkc0VtaXR0ZWQiOjUsImJ5dGVzRW1pdHRlZCI6MTI0OTcsInJlY29yZHNDb21taXR0ZWQiOjV9fSx7InN0cmVhbU5hbWUiOiJ0aGV0aXN0ZWFfcmVjaGFyZ2Vfc2hvcCIsInN0YXRzIjp7InJlY29yZHNFbWl0dGVkIjoxLCJieXRlc0VtaXR0ZWQiOjE0MDAsInJlY29yZHNDb21taXR0ZWQiOjF9fSx7InN0cmVhbU5hbWUiOiJ0aGV0aXN0ZWFfcmVjaGFyZ2VfcHJvZHVjdHMiLCJzdGF0cyI6eyJyZWNvcmRzRW1pdHRlZCI6OSwiYnl0ZXNFbWl0dGVkIjoxMDgwMiwicmVjb3Jkc0NvbW1pdHRlZCI6OX19LHsic3RyZWFtTmFtZSI6InRoZXRpc3RlYV9yZWNoYXJnZV9jdXN0b21lcnMiLCJzdGF0cyI6eyJyZWNvcmRzRW1pdHRlZCI6MSwiYnl0ZXNFbWl0dGVkIjo3NzQsInJlY29yZHNDb21taXR0ZWQiOjF9fSx7InN0cmVhbU5hbWUiOiJ0aGV0aXN0ZWFfcmVjaGFyZ2Vfb3JkZXJzIiwic3RhdHMiOnsicmVjb3Jkc0VtaXR0ZWQiOjEsImJ5dGVzRW1pdHRlZCI6Mjk1OSwicmVjb3Jkc0NvbW1pdHRlZCI6MX19LHsic3RyZWFtTmFtZSI6InRoZXRpc3RlYV9yZWNoYXJnZV9vbmV0aW1lcyIsInN0YXRzIjp7InJlY29yZHNFbWl0dGVkIjoxLCJieXRlc0VtaXR0ZWQiOjQzNSwicmVjb3Jkc0NvbW1pdHRlZCI6MX19LHsic3RyZWFtTmFtZSI6InRoZXRpc3RlYV9yZWNoYXJnZV9zdWJzY3JpcHRpb25zIiwic3RhdHMiOnsicmVjb3Jkc0VtaXR0ZWQiOjQsImJ5dGVzRW1pdHRlZCI6MzY0NSwicmVjb3Jkc0NvbW1pdHRlZCI6NH19XX0sIm5vcm1hbGl6YXRpb25TdW1tYXJ5Ijp7InN0YXJ0VGltZSI6MTY1ODI2NTk5NzE4NywiZW5kVGltZSI6MTY1ODI2NjE1NzE4NX0sInN0YXRlIjp7InN0YXRlIjp7Im9yZGVycyI6eyJ1cGRhdGVkX2F0IjoiMjAyMi0wNy0xOVQwMDowODowOCJ9LCJjaGFyZ2VzIjp7InVwZGF0ZWRfYXQiOiIyMDIyLTA3LTE5VDE2OjEzOjI0In0sIm9uZXRpbWVzIjp7InVwZGF0ZWRfYXQiOiIyMDIyLTAzLTIyVDE4OjExOjMwIn0sImN1c3RvbWVycyI6eyJ1cGRhdGVkX2F0IjoiMjAyMi0wNy0xOVQwMDoxMjozMiJ9LCJkaXNjb3VudHMiOnsidXBkYXRlZF9hdCI6IjIwMjItMDctMThUMjI6MDc6MTQifSwic3Vic2NyaXB0aW9ucyI6eyJ1cGRhdGVkX2F0IjoiMjAyMi0wNy0xOVQxNjoxMzoyMiJ9fX0sIm91dHB1dF9jYXRhbG9nIjp7InN0cmVhbXMiOlt7InN0cmVhbSI6eyJuYW1lIjoidGhldGlzdGVhX3JlY2hhcmdlX2NoYXJnZXMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInRhZ3MiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0eXBlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZW1haWwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJlcnJvciI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInRheF9saW5lcyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciLCJudW1iZXIiXX0sInRvdGFsX3RheCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciLCJudW1iZXIiXX0sImFkZHJlc3NfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImVycm9yX3R5cGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGluZV9pdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwicmV0cnlfZGF0ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJjdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ0b3RhbF9wcmljZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByb2Nlc3NlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInNjaGVkdWxlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInRvdGFsX3dlaWdodCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJjdXN0b21lcl9oYXNoIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidG90YWxfcmVmdW5kcyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFuYWx5dGljc19kYXRhIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdfSwiY2xpZW50X2RldGFpbHMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJkaXNjb3VudF9jb2RlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjoib2JqZWN0In19LCJwcm9jZXNzb3JfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInNoaXBwaW5nX2xpbmVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOiJvYmplY3QifX0sInN1YnRvdGFsX3ByaWNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidHJhbnNhY3Rpb25faWQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2FkZHJlc3MiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiemlwIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY2l0eSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInBob25lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY29tcGFueSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNvdW50cnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJhZGRyZXNzMSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFkZHJlc3MyIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicHJvdmluY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjYXJ0X25vdGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJsYXN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiZmlyc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJjdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJkaXNjb3VudF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJub3RlX2F0dHJpYnV0ZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX0sInNoaXBwaW5nX2xpbmVzX292ZXJyaWRlIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19fX0sIm5vdGVfYXR0cmlidXRlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic2hpcG1lbnRzX2NvdW50Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInRvdGFsX2Rpc2NvdW50cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInNoaXBwaW5nX2FkZHJlc3MiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiemlwIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY2l0eSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInBob25lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY29tcGFueSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNvdW50cnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJhZGRyZXNzMSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFkZHJlc3MyIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicHJvdmluY2UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjYXJ0X25vdGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJsYXN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiZmlyc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJjdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJkaXNjb3VudF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJub3RlX2F0dHJpYnV0ZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX0sInNoaXBwaW5nX2xpbmVzX292ZXJyaWRlIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19fX0sInNob3BpZnlfb3JkZXJfaWQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJudW1iZXJfdGltZXNfdHJpZWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiaGFzX3VuY29tbWl0ZWRfY2hhbmdlcyI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJ0b3RhbF9saW5lX2l0ZW1zX3ByaWNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGFzdF9jaGFyZ2VfYXR0ZW1wdF9kYXRlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwic2hvcGlmeV92YXJpYW50X2lkX25vdF9mb3VuZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIiwic3RyaW5nIl19fX0sInN1cHBvcnRlZF9zeW5jX21vZGVzIjpbImZ1bGxfcmVmcmVzaCIsImluY3JlbWVudGFsIl0sInNvdXJjZV9kZWZpbmVkX2N1cnNvciI6dHJ1ZSwiZGVmYXVsdF9jdXJzb3JfZmllbGQiOlsidXBkYXRlZF9hdCJdLCJzb3VyY2VfZGVmaW5lZF9wcmltYXJ5X2tleSI6W1siaWQiXV19LCJzeW5jX21vZGUiOiJpbmNyZW1lbnRhbCIsImN1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sImRlc3RpbmF0aW9uX3N5bmNfbW9kZSI6ImFwcGVuZF9kZWR1cCIsInByaW1hcnlfa2V5IjpbWyJpZCJdXX0seyJzdHJlYW0iOnsibmFtZSI6InRoZXRpc3RlYV9yZWNoYXJnZV9jb2xsZWN0aW9ucyIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9fX0sInN1cHBvcnRlZF9zeW5jX21vZGVzIjpbImZ1bGxfcmVmcmVzaCJdLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6W10sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImZ1bGxfcmVmcmVzaCIsImN1cnNvcl9maWVsZCI6W10sImRlc3RpbmF0aW9uX3N5bmNfbW9kZSI6Im92ZXJ3cml0ZSIsInByaW1hcnlfa2V5IjpbWyJpZCJdXX0seyJzdHJlYW0iOnsibmFtZSI6InRoZXRpc3RlYV9yZWNoYXJnZV9jdXN0b21lcnMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiaGFzaCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImVtYWlsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic3RhdHVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiYmlsbGluZ196aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2NpdHkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX3Bob25lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYW5hbHl0aWNzX2RhdGEiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJwcm9jZXNzb3JfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfY29tcGFueSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfY291bnRyeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzczEiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2FkZHJlc3MyIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19wcm92aW5jZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFjY2VwdHNfbWFya2V0aW5nIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImJpbGxpbmdfbGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19maXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hvcGlmeV9jdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm51bWJlcl9zdWJzY3JpcHRpb25zIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInN0cmlwZV9jdXN0b21lcl90b2tlbiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImhhc192YWxpZF9wYXltZW50X21ldGhvZCI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJmaXJzdF9jaGFyZ2VfcHJvY2Vzc2VkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiaGFzX2NhcmRfZXJyb3JfaW5fZHVubmluZyI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJudW1iZXJfYWN0aXZlX3N1YnNjcmlwdGlvbnMiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwicmVhc29uX3BheW1lbnRfbWV0aG9kX25vdF92YWxpZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoidGhldGlzdGVhX3JlY2hhcmdlX2Rpc2NvdW50cyIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJjb2RlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidmFsdWUiOnsidHlwZSI6WyJudWxsIiwibnVtYmVyIl19LCJzdGF0dXMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJlbmRzX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiZHVyYXRpb24iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzdGFydHNfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJhcHBsaWVzX3RvIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInRpbWVzX3VzZWQiOnsidHlwZSI6WyJudWxsIiwibnVtYmVyIl19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwidXNhZ2VfbGltaXQiOnsidHlwZSI6WyJudWxsIiwibnVtYmVyIl19LCJhcHBsaWVzX3RvX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImRpc2NvdW50X3R5cGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjaGFubmVsX3NldHRpbmdzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7ImFwaSI6eyJ0eXBlIjoib2JqZWN0IiwicHJvcGVydGllcyI6eyJjYW5fYXBwbHkiOnsidHlwZSI6ImJvb2xlYW4ifX19LCJjaGVja291dF9wYWdlIjp7InR5cGUiOiJvYmplY3QiLCJwcm9wZXJ0aWVzIjp7ImNhbl9hcHBseSI6eyJ0eXBlIjoiYm9vbGVhbiJ9fX0sImN1c3RvbWVyX3BvcnRhbCI6eyJ0eXBlIjoib2JqZWN0IiwicHJvcGVydGllcyI6eyJjYW5fYXBwbHkiOnsidHlwZSI6ImJvb2xlYW4ifX19LCJtZXJjaGFudF9wb3J0YWwiOnsidHlwZSI6Im9iamVjdCIsInByb3BlcnRpZXMiOnsiY2FuX2FwcGx5Ijp7InR5cGUiOiJib29sZWFuIn19fX19LCJvbmNlX3Blcl9jdXN0b21lciI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJhcHBsaWVzX3RvX3Jlc291cmNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZHVyYXRpb25fdXNhZ2VfbGltaXQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiZXh0ZXJuYWxfZGlzY291bnRfaWQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJhcHBsaWVzX3RvX3Byb2R1Y3RfdHlwZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImV4dGVybmFsX2Rpc2NvdW50X3NvdXJjZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByZXJlcXVpc2l0ZV9zdWJ0b3RhbF9taW4iOnsidHlwZSI6WyJudWxsIiwibnVtYmVyIl19LCJmaXJzdF90aW1lX2N1c3RvbWVyX3Jlc3RyaWN0aW9uIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJzdXBwb3J0ZWRfc3luY19tb2RlcyI6WyJmdWxsX3JlZnJlc2giLCJpbmNyZW1lbnRhbCJdLCJzb3VyY2VfZGVmaW5lZF9jdXJzb3IiOnRydWUsImRlZmF1bHRfY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwic291cmNlX2RlZmluZWRfcHJpbWFyeV9rZXkiOltbImlkIl1dfSwic3luY19tb2RlIjoiaW5jcmVtZW50YWwiLCJjdXJzb3JfZmllbGQiOlsidXBkYXRlZF9hdCJdLCJkZXN0aW5hdGlvbl9zeW5jX21vZGUiOiJhcHBlbmRfZGVkdXAiLCJwcmltYXJ5X2tleSI6W1siaWQiXV19LHsic3RyZWFtIjp7Im5hbWUiOiJ0aGV0aXN0ZWFfcmVjaGFyZ2Vfb25ldGltZXMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic2t1Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicHJpY2UiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciIsInN0cmluZyIsIm51bWJlciJdfSwic3RhdHVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicXVhbnRpdHkiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiYWRkcmVzc19pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIiwic3RyaW5nIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwicHJvcGVydGllcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiLCJzdHJpbmciXX0sInByb2R1Y3RfdGl0bGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ2YXJpYW50X3RpdGxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hvcGlmeV9wcm9kdWN0X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInNob3BpZnlfdmFyaWFudF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJyZWNoYXJnZV9wcm9kdWN0X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5leHRfY2hhcmdlX3NjaGVkdWxlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoidGhldGlzdGVhX3JlY2hhcmdlX29yZGVycyIsImpzb25fc2NoZW1hIjp7InR5cGUiOiJvYmplY3QiLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJoYXNoIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInRhZ3MiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ0eXBlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZW1haWwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzdGF0dXMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjdXJyZW5jeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImN1c3RvbWVyIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImhhc2giOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJlbWFpbCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImJpbGxpbmdfemlwIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19jaXR5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19waG9uZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByb2Nlc3Nvcl90eXBlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19jb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19jb3VudHJ5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19hZGRyZXNzMSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX3Byb3ZpbmNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmlsbGluZ19sYXN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiaWxsaW5nX2ZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaG9waWZ5X2N1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibnVtYmVyX3N1YnNjcmlwdGlvbnMiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiaGFzX3ZhbGlkX3BheW1lbnRfbWV0aG9kIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImZpcnN0X2NoYXJnZV9wcm9jZXNzZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJoYXNfY2FyZF9lcnJvcl9pbl9kdW5uaW5nIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sIm51bWJlcl9hY3RpdmVfc3Vic2NyaXB0aW9ucyI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJyZWFzb25fcGF5bWVudF9tZXRob2Rfbm90X3ZhbGlkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJyZWRhY3RlZCI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJjaGFyZ2VfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGF4X2xpbmVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJ0b3RhbF90YXgiOnsidHlwZSI6WyJudWxsIiwibnVtYmVyIiwic3RyaW5nIl19LCJhZGRyZXNzX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImJyb3dzZXJfaXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjcmVhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiZmlyc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImlzX3ByZXBhaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibGluZV9pdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjoib2JqZWN0IiwicHJvcGVydGllcyI6eyJza3UiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJncmFtcyI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sInByaWNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidGl0bGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJpbWFnZXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJxdWFudGl0eSI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJwcm9wZXJ0aWVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJ2YXJpYW50X3RpdGxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic3Vic2NyaXB0aW9uX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInNob3BpZnlfcHJvZHVjdF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciLCJpbnRlZ2VyIl19LCJzaG9waWZ5X3ZhcmlhbnRfaWQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIiwiaW50ZWdlciJdfSwiZXh0ZXJuYWxfaW52ZW50b3J5X3BvbGljeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fX0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJjdXN0b21lcl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ0b3RhbF9wcmljZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByb2Nlc3NlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInNjaGVkdWxlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInNoaXBwZWRfZGF0ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInRvdGFsX3dlaWdodCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJjaGFyZ2Vfc3RhdHVzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidG90YWxfcmVmdW5kcyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImRpc2NvdW50X2NvZGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJzaGlwcGluZ19saW5lcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic3VidG90YWxfcHJpY2UiOnsidHlwZSI6WyJudWxsIiwibnVtYmVyIiwic3RyaW5nIl19LCJ0cmFuc2FjdGlvbl9pZCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJpbGxpbmdfYWRkcmVzcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJpZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJ6aXAiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjaXR5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwicGhvbmUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb21wYW55Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY291bnRyeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFkZHJlc3MxIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczIiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcm92aW5jZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNhcnRfbm90ZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImxhc3RfbmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNyZWF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJmaXJzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidXBkYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImN1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImRpc2NvdW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5vdGVfYXR0cmlidXRlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfSwic2hpcHBpbmdfbGluZXNfb3ZlcnJpZGUiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX19fSwibm90ZV9hdHRyaWJ1dGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJ0b3RhbF9kaXNjb3VudHMiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaGlwcGluZ19hZGRyZXNzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInppcCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNpdHkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwaG9uZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNvbXBhbnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjb3VudHJ5Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzczEiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJhZGRyZXNzMiI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInByb3ZpbmNlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY2FydF9ub3RlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibGFzdF9uYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImZpcnN0X25hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiY3VzdG9tZXJfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiZGlzY291bnRfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibm90ZV9hdHRyaWJ1dGVzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il19LCJzaGlwcGluZ19saW5lc19vdmVycmlkZSI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdfX19LCJzaG9waWZ5X29yZGVyX2lkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYWRkcmVzc19pc19hY3RpdmUiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwicGF5bWVudF9wcm9jZXNzb3IiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaG9waWZ5X2NhcnRfdG9rZW4iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJzaG9waWZ5X2N1c3RvbWVyX2lkIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwic2hvcGlmeV9vcmRlcl9udW1iZXIiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwidG90YWxfbGluZV9pdGVtc19wcmljZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoidGhldGlzdGVhX3JlY2hhcmdlX3Byb2R1Y3RzIiwianNvbl9zY2hlbWEiOnsidHlwZSI6Im9iamVjdCIsIiRzY2hlbWEiOiJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInRpdGxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiaGFuZGxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiaW1hZ2VzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sInVwZGF0ZWRfYXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl0sImZvcm1hdCI6ImRhdGUtdGltZSJ9LCJjb2xsZWN0aW9uX2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImRpc2NvdW50X3R5cGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJkaXNjb3VudF9hbW91bnQiOnsidHlwZSI6WyJudWxsIiwibnVtYmVyIl19LCJvcmRlcl9kYXlfb2Zfd2VlayI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJjdXRvZmZfZGF5X29mX3dlZWsiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwib3JkZXJfZGF5X29mX21vbnRoIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInNob3BpZnlfcHJvZHVjdF9pZCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJjdXRvZmZfZGF5X29mX21vbnRoIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm9yZGVyX2ludGVydmFsX3VuaXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJtb2RpZmlhYmxlX3Byb3BlcnRpZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX0sInN1YnNjcmlwdGlvbl9kZWZhdWx0cyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXX0sImNoYXJnZV9pbnRlcnZhbF9mcmVxdWVuY3kiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic3RvcmVmcm9udF9wdXJjaGFzZV9vcHRpb25zIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwib3JkZXJfaW50ZXJ2YWxfZnJlcXVlbmN5X29wdGlvbnMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXX0sImV4cGlyZV9hZnRlcl9zcGVjaWZpY19udW1iZXJfb2ZfY2hhcmdlcyI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19fX0sInN1cHBvcnRlZF9zeW5jX21vZGVzIjpbImZ1bGxfcmVmcmVzaCJdLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6W10sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImZ1bGxfcmVmcmVzaCIsImN1cnNvcl9maWVsZCI6W10sImRlc3RpbmF0aW9uX3N5bmNfbW9kZSI6Im92ZXJ3cml0ZSIsInByaW1hcnlfa2V5IjpbWyJpZCJdXX0seyJzdHJlYW0iOnsibmFtZSI6InRoZXRpc3RlYV9yZWNoYXJnZV9zaG9wIiwianNvbl9zY2hlbWEiOnsidHlwZSI6Im9iamVjdCIsIiRzY2hlbWEiOiJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLCJwcm9wZXJ0aWVzIjp7InNob3AiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il19LCJzdG9yZSI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIl0sImRlZmF1bHRfY3Vyc29yX2ZpZWxkIjpbXSwic291cmNlX2RlZmluZWRfcHJpbWFyeV9rZXkiOltbInNob3AiXSxbInN0b3JlIl1dfSwic3luY19tb2RlIjoiZnVsbF9yZWZyZXNoIiwiY3Vyc29yX2ZpZWxkIjpbXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoib3ZlcndyaXRlIiwicHJpbWFyeV9rZXkiOltbInNob3AiXSxbInN0b3JlIl1dfSx7InN0cmVhbSI6eyJuYW1lIjoidGhldGlzdGVhX3JlY2hhcmdlX3N1YnNjcmlwdGlvbnMiLCJqc29uX3NjaGVtYSI6eyJ0eXBlIjoib2JqZWN0IiwiJHNjaGVtYSI6Imh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsInByb3BlcnRpZXMiOnsiaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic2t1Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZW1haWwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJwcmljZSI6eyJ0eXBlIjpbIm51bGwiLCJudW1iZXIiXX0sInN0YXR1cyI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sInF1YW50aXR5Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImFkZHJlc3NfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiY3JlYXRlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXSwiZm9ybWF0IjoiZGF0ZS10aW1lIn0sImlzX3ByZXBhaWQiOnsidHlwZSI6WyJudWxsIiwiYm9vbGVhbiJdfSwicHJvcGVydGllcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjoib2JqZWN0IiwicHJvcGVydGllcyI6eyJuYW1lIjp7InR5cGUiOiJzdHJpbmcifSwidmFsdWUiOnsidHlwZSI6WyJzdHJpbmciLCJpbnRlZ2VyIl19fX19LCJ1cGRhdGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiY3VzdG9tZXJfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiY2FuY2VsbGVkX2F0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdLCJmb3JtYXQiOiJkYXRlLXRpbWUifSwiaXNfc2tpcHBhYmxlIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX0sImlzX3N3YXBwYWJsZSI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJza3Vfb3ZlcnJpZGUiOnsidHlwZSI6WyJudWxsIiwiYm9vbGVhbiJdfSwiY29tbWl0X3VwZGF0ZSI6eyJ0eXBlIjpbIm51bGwiLCJib29sZWFuIl19LCJwcm9kdWN0X3RpdGxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwidmFyaWFudF90aXRsZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImFuYWx5dGljc19kYXRhIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdfSwib3JkZXJfZGF5X29mX3dlZWsiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwiaGFzX3F1ZXVlZF9jaGFyZ2VzIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm9yZGVyX2RheV9vZl9tb250aCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzaG9waWZ5X3Byb2R1Y3RfaWQiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwic2hvcGlmeV92YXJpYW50X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImNhbmNlbGxhdGlvbl9yZWFzb24iOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJtYXhfcmV0cmllc19yZWFjaGVkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm9yZGVyX2ludGVydmFsX3VuaXQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJyZWNoYXJnZV9wcm9kdWN0X2lkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5leHRfY2hhcmdlX3NjaGVkdWxlZF9hdCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm9yZGVyX2ludGVydmFsX2ZyZXF1ZW5jeSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImNoYXJnZV9pbnRlcnZhbF9mcmVxdWVuY3kiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJjYW5jZWxsYXRpb25fcmVhc29uX2NvbW1lbnRzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZXhwaXJlX2FmdGVyX3NwZWNpZmljX251bWJlcl9vZl9jaGFyZ2VzIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX19fSwic3VwcG9ydGVkX3N5bmNfbW9kZXMiOlsiZnVsbF9yZWZyZXNoIiwiaW5jcmVtZW50YWwiXSwic291cmNlX2RlZmluZWRfY3Vyc29yIjp0cnVlLCJkZWZhdWx0X2N1cnNvcl9maWVsZCI6WyJ1cGRhdGVkX2F0Il0sInNvdXJjZV9kZWZpbmVkX3ByaW1hcnlfa2V5IjpbWyJpZCJdXX0sInN5bmNfbW9kZSI6ImluY3JlbWVudGFsIiwiY3Vyc29yX2ZpZWxkIjpbInVwZGF0ZWRfYXQiXSwiZGVzdGluYXRpb25fc3luY19tb2RlIjoiYXBwZW5kX2RlZHVwIiwicHJpbWFyeV9rZXkiOltbImlkIl1dfV19LCJmYWlsdXJlcyI6W119fQ==" + "data": "eyJqb2JJZCI6MSwiYXR0ZW1wdE51bWJlciI6MCwiY29ubmVjdGlvbklkIjoiYjYxZDVhY2QtYmY3My00ZWU3LThkNDYtNjhmNDJkOTUzZjI4Iiwic3RhbmRhcmRTeW5jT3V0cHV0Ijp7InN0YW5kYXJkU3luY1N1bW1hcnkiOnsic3RhdHVzIjoiY29tcGxldGVkIiwicmVjb3Jkc1N5bmNlZCI6MSwiYnl0ZXNTeW5jZWQiOjIyNzE1LCJzdGFydFRpbWUiOjE2NzI5NTg0MDg2NjUsImVuZFRpbWUiOjE2NzI5NTg0MTE4MTMsInRvdGFsU3RhdHMiOnsiYnl0ZXNFbWl0dGVkIjoyMjcxNSwiZGVzdGluYXRpb25TdGF0ZU1lc3NhZ2VzRW1pdHRlZCI6MCwiZGVzdGluYXRpb25Xcml0ZUVuZFRpbWUiOjE2NzI5NTg0MTE4MTEsImRlc3RpbmF0aW9uV3JpdGVTdGFydFRpbWUiOjE2NzI5NTg0MDg3MzYsIm1lYW5TZWNvbmRzQmVmb3JlU291cmNlU3RhdGVNZXNzYWdlRW1pdHRlZCI6MCwibWF4U2Vjb25kc0JlZm9yZVNvdXJjZVN0YXRlTWVzc2FnZUVtaXR0ZWQiOjAsIm1heFNlY29uZHNCZXR3ZWVuU3RhdGVNZXNzYWdlRW1pdHRlZGFuZENvbW1pdHRlZCI6MCwibWVhblNlY29uZHNCZXR3ZWVuU3RhdGVNZXNzYWdlRW1pdHRlZGFuZENvbW1pdHRlZCI6MCwicmVjb3Jkc0VtaXR0ZWQiOjEsInJlY29yZHNDb21taXR0ZWQiOjEsInJlcGxpY2F0aW9uRW5kVGltZSI6MTY3Mjk1ODQxMTgxMiwicmVwbGljYXRpb25TdGFydFRpbWUiOjE2NzI5NTg0MDg2NjUsInNvdXJjZVJlYWRFbmRUaW1lIjoxNjcyOTU4NDExNzAwLCJzb3VyY2VSZWFkU3RhcnRUaW1lIjoxNjcyOTU4NDA4NzAwLCJzb3VyY2VTdGF0ZU1lc3NhZ2VzRW1pdHRlZCI6MH0sInN0cmVhbVN0YXRzIjpbeyJzdHJlYW1OYW1lIjoicG9rZW1vbiIsInN0YXRzIjp7ImJ5dGVzRW1pdHRlZCI6MjI3MTUsInJlY29yZHNFbWl0dGVkIjoxLCJyZWNvcmRzQ29tbWl0dGVkIjoxfX1dfSwib3V0cHV0X2NhdGFsb2ciOnsic3RyZWFtcyI6W3sic3RyZWFtIjp7Im5hbWUiOiJwb2tlbW9uIiwianNvbl9zY2hlbWEiOnsidHlwZSI6Im9iamVjdCIsIiRzY2hlbWEiOiJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLCJwcm9wZXJ0aWVzIjp7ImlkIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJmb3JtcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ1cmwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19fSwibW92ZXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsibW92ZSI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ1cmwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJ2ZXJzaW9uX2dyb3VwX2RldGFpbHMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidmVyc2lvbl9ncm91cCI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ1cmwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJsZXZlbF9sZWFybmVkX2F0Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIm1vdmVfbGVhcm5fbWV0aG9kIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX19fX19fX0sIm9yZGVyIjp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInN0YXRzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InN0YXQiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwiZWZmb3J0Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImJhc2Vfc3RhdCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19fX19LCJ0eXBlcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJzbG90Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInR5cGUiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fX19fSwiaGVpZ2h0Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sIndlaWdodCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19LCJzcGVjaWVzIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InVybCI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sIm5hbWUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sInNwcml0ZXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsiYmFja19zaGlueSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX0sImJhY2tfZmVtYWxlIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiZnJvbnRfc2hpbnkiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJiYWNrX2RlZmF1bHQiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJmcm9udF9mZW1hbGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJmcm9udF9kZWZhdWx0Ijp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwiYmFja19zaGlueV9mZW1hbGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJmcm9udF9zaGlueV9mZW1hbGUiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19fX0sImFiaWxpdGllcyI6eyJ0eXBlIjpbIm51bGwiLCJhcnJheSJdLCJpdGVtcyI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJzbG90Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sImFiaWxpdHkiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwiaXNfaGlkZGVuIjp7InR5cGUiOlsibnVsbCIsImJvb2xlYW4iXX19fX0sImhlbGRfaXRlbXMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsiaXRlbSI6eyJ0eXBlIjpbIm51bGwiLCJvYmplY3QiXSwicHJvcGVydGllcyI6eyJ1cmwiOnsidHlwZSI6WyJudWxsIiwic3RyaW5nIl19LCJuYW1lIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJ2ZXJzaW9uX2RldGFpbHMiOnsidHlwZSI6WyJudWxsIiwiYXJyYXkiXSwiaXRlbXMiOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsicmFyaXR5Ijp7InR5cGUiOlsibnVsbCIsImludGVnZXIiXX0sInZlcnNpb24iOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fX19fX19fSwiaXNfZGVmYXVsdCAiOnsidHlwZSI6WyJudWxsIiwiYm9vbGVhbiJdfSwiZ2FtZV9pbmRpY2VzIjp7InR5cGUiOlsibnVsbCIsImFycmF5Il0sIml0ZW1zIjp7InR5cGUiOlsibnVsbCIsIm9iamVjdCJdLCJwcm9wZXJ0aWVzIjp7InZlcnNpb24iOnsidHlwZSI6WyJudWxsIiwib2JqZWN0Il0sInByb3BlcnRpZXMiOnsidXJsIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfSwibmFtZSI6eyJ0eXBlIjpbIm51bGwiLCJzdHJpbmciXX19fSwiZ2FtZV9pbmRleCI6eyJ0eXBlIjpbIm51bGwiLCJpbnRlZ2VyIl19fX19LCJiYXNlX2V4cGVyaWVuY2UiOnsidHlwZSI6WyJudWxsIiwiaW50ZWdlciJdfSwibG9jYXRpb25fYXJlYV9lbmNvdW50ZXJzIjp7InR5cGUiOlsibnVsbCIsInN0cmluZyJdfX19LCJzdXBwb3J0ZWRfc3luY19tb2RlcyI6WyJmdWxsX3JlZnJlc2giXSwiZGVmYXVsdF9jdXJzb3JfZmllbGQiOltdLCJzb3VyY2VfZGVmaW5lZF9wcmltYXJ5X2tleSI6W119LCJzeW5jX21vZGUiOiJmdWxsX3JlZnJlc2giLCJjdXJzb3JfZmllbGQiOltdLCJkZXN0aW5hdGlvbl9zeW5jX21vZGUiOiJvdmVyd3JpdGUiLCJwcmltYXJ5X2tleSI6W119XX0sImZhaWx1cmVzIjpbXX19" } ] }, @@ -1458,52 +1719,50 @@ "scheduleToStartTimeout": "0s", "startToCloseTimeout": "120s", "heartbeatTimeout": "30s", - "workflowTaskCompletedEventId": "70", + "workflowTaskCompletedEventId": "81", "retryPolicy": { + "nonRetryableErrorTypes": [], "initialInterval": "30s", "backoffCoefficient": 2, "maximumInterval": "600s", - "maximumAttempts": 5, - "nonRetryableErrorTypes": [] + "maximumAttempts": 5 } } }, { - "eventId": "72", - "eventTime": "2022-07-19T21:29:17.937824766Z", + "eventId": "83", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "ActivityTaskStarted", - "version": "1011", - "taskId": "375403232", + "version": "0", + "taskId": "5243068", "activityTaskStartedEventAttributes": { - "scheduledEventId": "71", - "identity": "1@airbyte-worker-74797b7bc-4g4wc", - "requestId": "816dc81a-6e91-42cc-a3ff-fa6f34e87db1", - "attempt": 1, - "lastFailure": null + "scheduledEventId": "82", + "identity": "1@0a5c2d703445", + "requestId": "0020f434-cf91-4a39-a68b-bcfe3968d226", + "attempt": 1 } }, { - "eventId": "73", - "eventTime": "2022-07-19T21:29:18.155968322Z", + "eventId": "84", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "ActivityTaskCompleted", - "version": "1011", - "taskId": "375403233", + "version": "0", + "taskId": "5243069", "activityTaskCompletedEventAttributes": { - "result": null, - "scheduledEventId": "71", - "startedEventId": "72", - "identity": "1@airbyte-worker-74797b7bc-4g4wc" + "scheduledEventId": "82", + "startedEventId": "83", + "identity": "1@0a5c2d703445" } }, { - "eventId": "74", - "eventTime": "2022-07-19T21:29:18.155976032Z", + "eventId": "85", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "375403234", + "version": "0", + "taskId": "5243070", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-gjw7f:0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -1511,73 +1770,71 @@ } }, { - "eventId": "75", - "eventTime": "2022-07-19T21:29:18.174332100Z", + "eventId": "86", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "375403238", + "version": "0", + "taskId": "5243074", "workflowTaskStartedEventAttributes": { - "scheduledEventId": "74", - "identity": "0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", - "requestId": "2c9d18d8-39f6-4be6-9deb-f0f0634178eb" + "scheduledEventId": "85", + "identity": "1@0a5c2d703445", + "requestId": "eafd3554-609d-4e79-9cbd-9125c7860a2d" } }, { - "eventId": "76", - "eventTime": "2022-07-19T21:29:18.279850302Z", + "eventId": "87", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "375403242", + "version": "0", + "taskId": "5243077", "workflowTaskCompletedEventAttributes": { - "scheduledEventId": "74", - "startedEventId": "75", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", + "scheduledEventId": "85", + "startedEventId": "86", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { - "eventId": "77", - "eventTime": "2022-07-19T21:29:18.279877503Z", + "eventId": "88", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "MarkerRecorded", - "version": "1011", - "taskId": "375403243", + "version": "0", + "taskId": "5243078", "markerRecordedEventAttributes": { - "markerName": "Version", "details": { - "changeId": { + "version": { "payloads": [ { "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "ImRlbGV0ZV9yZXNldF9qb2Jfc3RyZWFtcyI=" + "data": "MQ==" } ] }, - "version": { + "changeId": { "payloads": [ { "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "MQ==" + "data": "ImRlbGV0ZV9yZXNldF9qb2Jfc3RyZWFtcyI=" } ] } }, - "workflowTaskCompletedEventId": "76", - "header": null, - "failure": null + "markerName": "Version", + "workflowTaskCompletedEventId": "87" } }, { - "eventId": "78", - "eventTime": "2022-07-19T21:29:18.279889863Z", + "eventId": "89", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "ActivityTaskScheduled", - "version": "1011", - "taskId": "375403244", + "version": "0", + "taskId": "5243079", "activityTaskScheduledEventAttributes": { - "activityId": "b7137338-9d6d-3e95-b660-b5774abd389c", + "activityId": "ea67a7f2-d289-36e5-a002-b74840a36817", "activityType": { "name": "DeleteStreamResetRecordsForJob" }, @@ -1595,7 +1852,7 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJjb25uZWN0aW9uSWQiOiJkZjdlNjg4Ni00NTgxLTQwZWUtYmFlNS05ZTdmZjI3MWRhM2MiLCJqb2JJZCI6Mzc5NTY3fQ==" + "data": "eyJjb25uZWN0aW9uSWQiOiJiNjFkNWFjZC1iZjczLTRlZTctOGQ0Ni02OGY0MmQ5NTNmMjgiLCJqb2JJZCI6MX0=" } ] }, @@ -1603,52 +1860,50 @@ "scheduleToStartTimeout": "0s", "startToCloseTimeout": "120s", "heartbeatTimeout": "30s", - "workflowTaskCompletedEventId": "76", + "workflowTaskCompletedEventId": "87", "retryPolicy": { + "nonRetryableErrorTypes": [], "initialInterval": "30s", "backoffCoefficient": 2, "maximumInterval": "600s", - "maximumAttempts": 5, - "nonRetryableErrorTypes": [] + "maximumAttempts": 5 } } }, { - "eventId": "79", - "eventTime": "2022-07-19T21:29:18.295402556Z", + "eventId": "90", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "ActivityTaskStarted", - "version": "1011", - "taskId": "375403249", + "version": "0", + "taskId": "5243083", "activityTaskStartedEventAttributes": { - "scheduledEventId": "78", - "identity": "1@airbyte-worker-74797b7bc-4g4wc", - "requestId": "a18e2309-5f8f-499f-bb44-a6e53eed32bd", - "attempt": 1, - "lastFailure": null + "scheduledEventId": "89", + "identity": "1@0a5c2d703445", + "requestId": "5c59d9d0-4b89-4891-ad2a-357012abb74b", + "attempt": 1 } }, { - "eventId": "80", - "eventTime": "2022-07-19T21:29:18.349895424Z", + "eventId": "91", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "ActivityTaskCompleted", - "version": "1011", - "taskId": "375403250", + "version": "0", + "taskId": "5243084", "activityTaskCompletedEventAttributes": { - "result": null, - "scheduledEventId": "78", - "startedEventId": "79", - "identity": "1@airbyte-worker-74797b7bc-4g4wc" + "scheduledEventId": "89", + "startedEventId": "90", + "identity": "1@0a5c2d703445" } }, { - "eventId": "81", - "eventTime": "2022-07-19T21:29:18.349904924Z", + "eventId": "92", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "WorkflowTaskScheduled", - "version": "1011", - "taskId": "375403251", + "version": "0", + "taskId": "5243085", "workflowTaskScheduledEventAttributes": { "taskQueue": { - "name": "1@airbyte-worker-74797b7bc-gjw7f:0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", "kind": "Sticky" }, "startToCloseTimeout": "10s", @@ -1656,38 +1911,146 @@ } }, { - "eventId": "82", - "eventTime": "2022-07-19T21:29:18.367756868Z", + "eventId": "93", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "WorkflowTaskStarted", - "version": "1011", - "taskId": "375403255", + "version": "0", + "taskId": "5243089", "workflowTaskStartedEventAttributes": { - "scheduledEventId": "81", - "identity": "0cef8ce4-4ec4-460b-bd12-5b20fe01e88e", - "requestId": "7396b3e0-6c25-47a5-bc13-4efedd4d60f6" + "scheduledEventId": "92", + "identity": "1@0a5c2d703445", + "requestId": "ac4564e5-1dba-474c-b2ef-5891a2c7c1d3" } }, { - "eventId": "83", - "eventTime": "2022-07-19T21:29:18.429665477Z", + "eventId": "94", + "eventTime": "2023-01-05T22:40:11.000Z", "eventType": "WorkflowTaskCompleted", - "version": "1011", - "taskId": "375403259", + "version": "0", + "taskId": "5243092", "workflowTaskCompletedEventAttributes": { - "scheduledEventId": "81", - "startedEventId": "82", - "identity": "1@airbyte-worker-74797b7bc-gjw7f", + "scheduledEventId": "92", + "startedEventId": "93", + "identity": "1@0a5c2d703445", "binaryChecksum": "" } }, { - "eventId": "84", - "eventTime": "2022-07-19T21:29:18.429708288Z", + "eventId": "95", + "eventTime": "2023-01-05T22:40:11.000Z", + "eventType": "ActivityTaskScheduled", + "version": "0", + "taskId": "5243093", + "activityTaskScheduledEventAttributes": { + "activityId": "647de274-0e8d-3ad9-8803-9aa18baf7162", + "activityType": { + "name": "RecordWorkflowCountMetric" + }, + "namespace": "", + "taskQueue": { + "name": "CONNECTION_UPDATER", + "kind": "Normal" + }, + "header": { + "fields": {} + }, + "input": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "eyJjb25uZWN0aW9uVXBkYXRlcklucHV0Ijp7ImNvbm5lY3Rpb25JZCI6ImI2MWQ1YWNkLWJmNzMtNGVlNy04ZDQ2LTY4ZjQyZDk1M2YyOCIsImpvYklkIjoxLCJhdHRlbXB0SWQiOm51bGwsImZyb21GYWlsdXJlIjpmYWxzZSwiYXR0ZW1wdE51bWJlciI6MSwid29ya2Zsb3dTdGF0ZSI6bnVsbCwicmVzZXRDb25uZWN0aW9uIjpmYWxzZSwiZnJvbUpvYlJlc2V0RmFpbHVyZSI6ZmFsc2UsInNraXBTY2hlZHVsaW5nIjpmYWxzZX0sImZhaWx1cmVDYXVzZSI6bnVsbCwibWV0cmljTmFtZSI6IlRFTVBPUkFMX1dPUktGTE9XX1NVQ0NFU1MiLCJtZXRyaWNBdHRyaWJ1dGVzIjpudWxsfQ==" + } + ] + }, + "scheduleToCloseTimeout": "0s", + "scheduleToStartTimeout": "0s", + "startToCloseTimeout": "120s", + "heartbeatTimeout": "30s", + "workflowTaskCompletedEventId": "94", + "retryPolicy": { + "nonRetryableErrorTypes": [], + "initialInterval": "30s", + "backoffCoefficient": 2, + "maximumInterval": "600s", + "maximumAttempts": 5 + } + } + }, + { + "eventId": "96", + "eventTime": "2023-01-05T22:40:11.000Z", + "eventType": "ActivityTaskStarted", + "version": "0", + "taskId": "5243097", + "activityTaskStartedEventAttributes": { + "scheduledEventId": "95", + "identity": "1@0a5c2d703445", + "requestId": "92e910f5-b315-40d2-b9a9-9c2882a823ce", + "attempt": 1 + } + }, + { + "eventId": "97", + "eventTime": "2023-01-05T22:40:11.000Z", + "eventType": "ActivityTaskCompleted", + "version": "0", + "taskId": "5243098", + "activityTaskCompletedEventAttributes": { + "scheduledEventId": "95", + "startedEventId": "96", + "identity": "1@0a5c2d703445" + } + }, + { + "eventId": "98", + "eventTime": "2023-01-05T22:40:11.000Z", + "eventType": "WorkflowTaskScheduled", + "version": "0", + "taskId": "5243099", + "workflowTaskScheduledEventAttributes": { + "taskQueue": { + "name": "1@0a5c2d703445:6c1f280a-11bc-4300-b9a3-04e32d9019a3", + "kind": "Sticky" + }, + "startToCloseTimeout": "10s", + "attempt": 1 + } + }, + { + "eventId": "99", + "eventTime": "2023-01-05T22:40:11.000Z", + "eventType": "WorkflowTaskStarted", + "version": "0", + "taskId": "5243103", + "workflowTaskStartedEventAttributes": { + "scheduledEventId": "98", + "identity": "1@0a5c2d703445", + "requestId": "1fb9c055-bdb6-48e4-bcd5-864a20bcdba8" + } + }, + { + "eventId": "100", + "eventTime": "2023-01-05T22:40:12.000Z", + "eventType": "WorkflowTaskCompleted", + "version": "0", + "taskId": "5243106", + "workflowTaskCompletedEventAttributes": { + "scheduledEventId": "98", + "startedEventId": "99", + "identity": "1@0a5c2d703445", + "binaryChecksum": "" + } + }, + { + "eventId": "101", + "eventTime": "2023-01-05T22:40:12.000Z", "eventType": "WorkflowExecutionContinuedAsNew", - "version": "1011", - "taskId": "375403260", + "version": "0", + "taskId": "5243107", "workflowExecutionContinuedAsNewEventAttributes": { - "newExecutionRunId": "187f4581-a934-47b4-a8e7-65755ae05a47", + "newExecutionRunId": "eb885146-8519-4119-9acf-51f90f031cad", "workflowType": { "name": "ConnectionManagerWorkflow" }, @@ -1701,22 +2064,17 @@ "metadata": { "encoding": "anNvbi9wbGFpbg==" }, - "data": "eyJjb25uZWN0aW9uSWQiOiJkZjdlNjg4Ni00NTgxLTQwZWUtYmFlNS05ZTdmZjI3MWRhM2MiLCJqb2JJZCI6bnVsbCwiYXR0ZW1wdElkIjpudWxsLCJmcm9tRmFpbHVyZSI6ZmFsc2UsImF0dGVtcHROdW1iZXIiOjEsIndvcmtmbG93U3RhdGUiOm51bGwsInJlc2V0Q29ubmVjdGlvbiI6ZmFsc2UsImZyb21Kb2JSZXNldEZhaWx1cmUiOmZhbHNlLCJza2lwU2NoZWR1bGluZyI6ZmFsc2V9" + "data": "eyJjb25uZWN0aW9uSWQiOiJiNjFkNWFjZC1iZjczLTRlZTctOGQ0Ni02OGY0MmQ5NTNmMjgiLCJqb2JJZCI6bnVsbCwiYXR0ZW1wdElkIjpudWxsLCJmcm9tRmFpbHVyZSI6ZmFsc2UsImF0dGVtcHROdW1iZXIiOjEsIndvcmtmbG93U3RhdGUiOm51bGwsInJlc2V0Q29ubmVjdGlvbiI6ZmFsc2UsImZyb21Kb2JSZXNldEZhaWx1cmUiOmZhbHNlLCJza2lwU2NoZWR1bGluZyI6ZmFsc2V9" } ] }, "workflowRunTimeout": "0s", "workflowTaskTimeout": "10s", - "workflowTaskCompletedEventId": "83", - "backoffStartInterval": null, + "workflowTaskCompletedEventId": "100", "initiator": "Unspecified", - "failure": null, - "lastCompletionResult": null, "header": { "fields": {} - }, - "memo": null, - "searchAttributes": null + } } } ] From de1a587584bbe370ff3fbdf01c18354e5eff88f5 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 6 Jan 2023 01:46:34 +0100 Subject: [PATCH 011/170] =?UTF-8?q?=F0=9F=AA=9F=F0=9F=8E=89=20Connector=20?= =?UTF-8?q?builder:=20Available=20inputs=20dropdown=20(#20983)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * improve some types * improve further * clean up a bit more * refactor loading state * move loading state up * remove isLoading references * remove unused props and make fetch connector error work * remove special component for name * remove top level state for unifinished flows * start removing uiwidget * Update airbyte-webapp/src/views/Connector/ConnectorCard/ConnectorCard.module.scss Co-authored-by: Tim Roes * remove undefined option for selected id * remove unused prop * fix types * remove uiwidget state * clean up * adjust comment * handle errors in a nice way * do not respect default on oneOf fields * rename to formblock * reduce re-renders * pass error to secure inputs * simplify and improve styling * align top * code review * remove comment * review comments * rename file * be strict about boolean values * add example * track form error in error boundary * review comments * handle unexpected cases better * enrich error with connector id * 🪟🎉 Add copy stream button (#20577) * add copy stream button * review comments * rename prop * 🪟🎉 Connector builder: Integrate connector form for test input (#20385) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * make connector form work in connector builder * fix small stuff * add warning label * review comments * adjust translation Co-authored-by: lmossman * use request_body_json instead of request_body_data * :window: :art: Move `Add` button into the line of Connector Builder key value list fields (#20699) * move add button into line * add stories for empty with control, and content + control * change button name to Control * 🪟🎉 Connector builder: Allow defining inputs (#20431) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * make connector form work in connector builder * wip * fix small stuff * add basic input UI * user inputs * make most of inputs configuration work * fix a bunch of stuff * handle unknown config types * add warning label * fix label * fix some styling * review comments * improve state management and error handling * handle stored form values that don't contain new fields properly * Update airbyte-webapp/src/locales/en.json Co-authored-by: Lake Mossman * Update airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx Co-authored-by: Lake Mossman * inputs editing weirdness * input form reset * using the Label component * 🪟🎉 Connector builder authentication (#20645) * allow auth configuration * check for conflicts with the inferred inputs * fix invisible inputs * reduce redundancy and hide advanced input options for inferred inputs * unnecessary validation * typo * unnecessary effect hook * build spec even for invalid forms but do not update stream list * fix keys * 🪟🎉 Connector builder: Session token and oauth authentication (#20712) * session token and oauth authentication * fill in session token variable * typos * make sure validation error does not go away * 🪟🎉 Connector builder: Always validate inputs form (#20664) * validate user input outside of form * review comments Co-authored-by: lmossman Co-authored-by: lmossman * fix merge conflict with dropdown prop being renamed to control * [Connector Builder] Add paginator (#20698) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * make connector form work in connector builder * wip * fix small stuff * add basic input UI * user inputs * make most of inputs configuration work * fix a bunch of stuff * handle unknown config types * add warning label * fix label * fix some styling * review comments * improve state management and error handling * allow auth configuration * check for conflicts with the inferred inputs * fix invisible inputs * handle stored form values that don't contain new fields properly * session token and oauth authentication * fill in session token variable * fix merge of default values * add primaryKey and cursorField to builder types, and consolidate default valeues to types.ts * add cursor and primary key fields to ui * save * add page size and token option inputs * fixes after rebase * add pagination * fix pagination types * handle empty field_name better * Update airbyte-webapp/src/locales/en.json Co-authored-by: Lake Mossman * Update airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx Co-authored-by: Lake Mossman * inputs editing weirdness * input form reset * using the Label component * reduce redundancy and hide advanced input options for inferred inputs * unnecessary validation * typo * unnecessary effect hook * build spec even for invalid forms but do not update stream list * typos * make sure validation error does not go away * make primary key and cursor optional, and reorder * save toggle group progress * fix style of toggle label * handle empty values better * fix page size/token option field validation and rendering * handle cursor pagination page size option correctly Co-authored-by: Joe Reuter * [Connector Builder] Add stream slicer (#20748) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * make connector form work in connector builder * wip * fix small stuff * add basic input UI * user inputs * make most of inputs configuration work * fix a bunch of stuff * handle unknown config types * add warning label * fix label * fix some styling * review comments * improve state management and error handling * allow auth configuration * check for conflicts with the inferred inputs * fix invisible inputs * handle stored form values that don't contain new fields properly * session token and oauth authentication * fill in session token variable * fix merge of default values * add primaryKey and cursorField to builder types, and consolidate default valeues to types.ts * add cursor and primary key fields to ui * save * add page size and token option inputs * fixes after rebase * add pagination * fix pagination types * handle empty field_name better * Update airbyte-webapp/src/locales/en.json Co-authored-by: Lake Mossman * Update airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx Co-authored-by: Lake Mossman * inputs editing weirdness * input form reset * using the Label component * reduce redundancy and hide advanced input options for inferred inputs * unnecessary validation * typo * unnecessary effect hook * build spec even for invalid forms but do not update stream list * typos * make sure validation error does not go away * make primary key and cursor optional, and reorder * save toggle group progress * fix style of toggle label * handle empty values better * fix page size/token option field validation and rendering * handle cursor pagination page size option correctly * save stream slicer progress * finish stream slicer * fix stream slicer fields and validation Co-authored-by: Joe Reuter * debounce form builder values update to reduce load * 🪟🔧 Connector builder: use new lowcode manifest (#20715) * use new manifest yaml * Update airbyte-webapp/src/components/connectorBuilder/types.ts Co-authored-by: Lake Mossman * use updated manifest types Co-authored-by: Lake Mossman * debounce validation as well * akways show stream test button in error state if there are errors * fix type of oauth input * available inputs dropdown * add validation schema for add stream form * validate all views on test click * add type to prevent console warning * improve styling * make sure padding is set correctly * make sure focus is set right * comment * use correct state hook * add tooltip to user input button and fix wording for new user input option Co-authored-by: Tim Roes Co-authored-by: lmossman --- .../Builder/AuthenticationSection.tsx | 21 +- .../connectorBuilder/Builder/BuilderField.tsx | 8 +- .../BuilderFieldWithInputs.module.scss | 63 ++++ .../Builder/BuilderFieldWithInputs.tsx | 108 +++++++ .../Builder/InjectRequestOptionFields.tsx | 3 +- .../Builder/InputsForm.module.scss | 12 + .../connectorBuilder/Builder/InputsForm.tsx | 278 +++++++++++++++++ .../Builder/InputsView.module.scss | 10 - .../connectorBuilder/Builder/InputsView.tsx | 295 ++---------------- .../Builder/KeyValueListField.module.scss | 4 + .../Builder/KeyValueListField.tsx | 19 +- .../Builder/PaginationSection.tsx | 5 +- .../Builder/StreamConfigView.tsx | 3 +- .../Builder/StreamSlicerSection.tsx | 23 +- .../src/components/ui/Input/Input.tsx | 6 +- .../src/components/ui/ListBox/ListBox.tsx | 11 +- airbyte-webapp/src/locales/en.json | 1 + 17 files changed, 557 insertions(+), 313 deletions(-) create mode 100644 airbyte-webapp/src/components/connectorBuilder/Builder/BuilderFieldWithInputs.module.scss create mode 100644 airbyte-webapp/src/components/connectorBuilder/Builder/BuilderFieldWithInputs.tsx create mode 100644 airbyte-webapp/src/components/connectorBuilder/Builder/InputsForm.module.scss create mode 100644 airbyte-webapp/src/components/connectorBuilder/Builder/InputsForm.tsx diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/AuthenticationSection.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/AuthenticationSection.tsx index c495defd1f80..03ad98fbc198 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/AuthenticationSection.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/AuthenticationSection.tsx @@ -1,5 +1,6 @@ import { BuilderCard } from "./BuilderCard"; import { BuilderField } from "./BuilderField"; +import { BuilderFieldWithInputs } from "./BuilderFieldWithInputs"; import { BuilderOneOf } from "./BuilderOneOf"; import { BuilderOptional } from "./BuilderOptional"; import { KeyValueListField } from "./KeyValueListField"; @@ -23,7 +24,7 @@ export const AuthenticationSection: React.FC = () => { }, children: ( <> - { }, children: ( <> - { label="Scopes" tooltip="Scopes to request" /> - - - - { }, children: ( <> - - - - void } | { type: "boolean"; onChange?: (newValue: boolean) => void } @@ -68,6 +71,7 @@ export const BuilderField: React.FC = ({ optional = false, readOnly, pattern, + adornment, ...props }) => { const [field, meta, helpers] = useField(path); @@ -104,10 +108,12 @@ export const BuilderField: React.FC = ({ } props.onChange?.(e.target.value); }} + className={props.className} type={props.type} value={field.value ?? ""} error={hasError} readOnly={readOnly} + adornment={adornment} /> )} {props.type === "array" && ( diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderFieldWithInputs.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderFieldWithInputs.module.scss new file mode 100644 index 000000000000..c64c3d15f6b1 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderFieldWithInputs.module.scss @@ -0,0 +1,63 @@ +@use "scss/variables"; +@use "scss/colors"; + +.button { + align-self: flex-end; + width: auto; + border: none; + background: none; + color: colors.$grey-300; + + &:hover { + background: none; + border: none; + color: colors.$blue-500; + } +} + +.selectedOption { + margin-top: variables.$spacing-lg; + background: none; + font-size: 12px; + color: colors.$blue-500; + + &:hover { + color: colors.$blue-900; + } + + & > span { + padding-top: 0; + padding-bottom: 0; + } +} + +.container { + position: absolute; + right: variables.$spacing-sm; + top: 0; + display: flex; + flex-direction: column; + max-width: 400px; + width: 100%; + + // make the element click through, but all children should behave as normal so the input can be clicked as normal + pointer-events: none; + + & > * { + pointer-events: all; + } +} + +.buttonContent { + background: none; + border: none; + display: flex; + gap: variables.$spacing-xs; + color: inherit; + align-items: center; + cursor: pointer; +} + +.inputWithHelper { + padding-right: 55px; +} diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderFieldWithInputs.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderFieldWithInputs.tsx new file mode 100644 index 000000000000..0751eda7e698 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderFieldWithInputs.tsx @@ -0,0 +1,108 @@ +import { faPlus, faUser } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useField } from "formik"; +import { useMemo, useState } from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { ListBox, ListBoxControlButtonProps, Option } from "components/ui/ListBox"; +import { Tooltip } from "components/ui/Tooltip"; + +import { useConnectorBuilderFormState } from "services/connectorBuilder/ConnectorBuilderStateService"; + +import { getInferredInputs } from "../types"; +import { BuilderField, BuilderFieldProps } from "./BuilderField"; +import styles from "./BuilderFieldWithInputs.module.scss"; +import { InputForm, newInputInEditing } from "./InputsForm"; + +export const BuilderFieldWithInputs: React.FC = (props) => { + const [field, , helpers] = useField(props.path); + + return ( + } + className={styles.inputWithHelper} + /> + ); +}; + +export const UserInputHelper = ({ + setValue, + currentValue, +}: { + setValue: (value: string) => void; + currentValue: string; +}) => { + const { formatMessage } = useIntl(); + const [modalOpen, setModalOpen] = useState(false); + const { builderFormValues } = useConnectorBuilderFormState(); + const listOptions = useMemo(() => { + const options: Array> = [ + ...builderFormValues.inputs, + ...getInferredInputs(builderFormValues), + ].map((input) => ({ + label: input.definition.title || input.key, + value: input.key, + })); + options.push({ + value: undefined, + label: formatMessage({ id: "connectorBuilder.inputModal.newTitle" }), + icon: , + }); + return options; + }, [builderFormValues, formatMessage]); + return ( + <> + + buttonClassName={styles.button} + optionClassName={styles.option} + className={styles.container} + selectedOptionClassName={styles.selectedOption} + controlButton={UserInputHelperControlButton} + selectedValue={undefined} + onSelect={(selectedValue) => { + if (selectedValue) { + setValue(`${currentValue || ""}{{ config['${selectedValue}'] }}`); + } else { + // This hack is necessary because listbox will put the focus back when the option list gets hidden, which conflicts with the auto-focus setting of the modal. + // As it's not possible to prevent listbox from forcing the focus back on the button component, this will wait until the focus went to the button, then opens the modal + // so it can move it to the first input + setTimeout(() => { + setModalOpen(true); + }, 50); + } + }} + options={listOptions} + /> + {modalOpen && ( + { + setModalOpen(false); + if (!newInput) { + return; + } + setValue(`${currentValue}{{ config['${newInput.key}'] }}`); + }} + /> + )} + + ); +}; + +const UserInputHelperControlButton: React.FC> = () => { + return ( + + {"{{"} + + {"}}"} +

+ } + placement="top" + > + + + ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/InjectRequestOptionFields.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/InjectRequestOptionFields.tsx index 3d8607eb85f9..426af9e421fb 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/InjectRequestOptionFields.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/InjectRequestOptionFields.tsx @@ -4,6 +4,7 @@ import { RequestOption } from "core/request/ConnectorManifest"; import { injectIntoValues } from "../types"; import { BuilderField } from "./BuilderField"; +import { BuilderFieldWithInputs } from "./BuilderFieldWithInputs"; interface InjectRequestOptionFieldsProps { path: string; @@ -37,7 +38,7 @@ export const InjectRequestOptionFields: React.FC tooltip={`Configures where the ${descriptor} should be set on the HTTP requests`} /> {field.value.inject_into !== "path" && ( - void; +}) => { + const { values, setFieldValue } = useFormikContext(); + const [inputs, , helpers] = useField("inputs"); + const inferredInputs = useMemo(() => getInferredInputs(values), [values]); + const usedKeys = useMemo( + () => [...inputs.value, ...inferredInputs].map((input) => input.key), + [inputs.value, inferredInputs] + ); + const inputInEditValidation = useMemo( + () => + yup.object().shape({ + // make sure key can only occur once + key: yup + .string() + .notOneOf( + inputInEditing?.isNew ? usedKeys : usedKeys.filter((key) => key !== inputInEditing?.key), + "connectorBuilder.duplicateFieldID" + ), + required: yup.bool(), + definition: yup.object().shape({ + title: yup.string().required("form.empty.error"), + }), + }), + [inputInEditing?.isNew, inputInEditing?.key, usedKeys] + ); + return ( + { + if (values.isInferredInputOverride) { + setFieldValue(`inferredInputOverrides.${values.key}`, values.definition); + onClose(); + } else { + const newInput = inputInEditingToFormInput(values); + helpers.setValue( + inputInEditing.isNew + ? [...inputs.value, newInput] + : inputs.value.map((input) => (input.key === inputInEditing.key ? newInput : input)) + ); + onClose(newInput); + } + }} + > + <> + + { + helpers.setValue(inputs.value.filter((input) => input.key !== inputInEditing.key)); + onClose(); + }} + onClose={() => { + onClose(); + }} + /> + + + ); +}; + +const InputModal = ({ + inputInEditing, + onClose, + onDelete, +}: { + inputInEditing: InputInEditing; + onDelete: () => void; + onClose: () => void; +}) => { + const isInferredInputOverride = inputInEditing.isInferredInputOverride; + const { isValid, values, setFieldValue, setTouched } = useFormikContext(); + + const { formatMessage } = useIntl(); + useEffectOnce(() => { + // key input is always touched so errors are shown right away as it will be auto-set by the user changing the title + setTouched({ key: true }); + }); + + return ( + + } + onClose={onClose} + > +
+ + { + if (!isInferredInputOverride) { + setFieldValue("key", sluggify(newValue || ""), true); + } + }} + label={formatMessage({ id: "connectorBuilder.inputModal.inputName" })} + tooltip={formatMessage({ id: "connectorBuilder.inputModal.inputNameTooltip" })} + /> + + + {values.type !== "unknown" && !isInferredInputOverride ? ( + <> + { + setFieldValue("definition.default", undefined); + }} + label={formatMessage({ id: "connectorBuilder.inputModal.type" })} + tooltip={formatMessage({ id: "connectorBuilder.inputModal.typeTooltip" })} + /> + {values.type === "enum" && ( + + )} + + + + {values.showDefaultValueField && ( + + )} + + + ) : ( + + {isInferredInputOverride ? ( + + ) : ( + + )} + + )} + + + {!inputInEditing.isNew && !inputInEditing.isInferredInputOverride && ( +
+ +
+ )} + + +
+
+
+ ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.module.scss index 36580839d42d..0e6eeed73291 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.module.scss +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.module.scss @@ -26,12 +26,6 @@ padding: variables.$spacing-xs; } -.inputForm { - gap: variables.$spacing-lg; - display: flex; - flex-direction: column; -} - .inputsDescription { margin-top: variables.$spacing-xl; margin-bottom: variables.$spacing-lg; @@ -44,7 +38,3 @@ .inputsCard { align-self: stretch; } - -.deleteButtonContainer { - flex-grow: 1; -} diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx index 77095bd805fc..293350c5c264 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/InputsView.tsx @@ -1,127 +1,38 @@ import { faGear, faPlus } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Form, Formik, useField, useFormikContext } from "formik"; -import { JSONSchema7 } from "json-schema"; +import { useFormikContext } from "formik"; import { useMemo, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { useEffectOnce } from "react-use"; -import * as yup from "yup"; import { Button } from "components/ui/Button"; import { Card } from "components/ui/Card"; -import { InfoBox } from "components/ui/InfoBox"; -import { Modal, ModalBody, ModalFooter } from "components/ui/Modal"; import { Text } from "components/ui/Text"; -import { FormikPatch } from "core/form/FormikPatch"; - import { BuilderFormInput, BuilderFormValues, getInferredInputs } from "../types"; import { BuilderConfigView } from "./BuilderConfigView"; -import { BuilderField } from "./BuilderField"; +import { InputForm, InputInEditing, newInputInEditing } from "./InputsForm"; import styles from "./InputsView.module.scss"; const supportedTypes = ["string", "integer", "number", "array", "boolean", "enum", "unknown"] as const; -interface InputInEditing { - key: string; - definition: JSONSchema7; - required: boolean; - isNew?: boolean; - showDefaultValueField: boolean; - type: typeof supportedTypes[number]; - isInferredInputOverride: boolean; -} - -function sluggify(str: string) { - return str.toLowerCase().replaceAll(/[^a-zA-Z\d]/g, "_"); -} - -function newInputInEditing(): InputInEditing { - return { - key: "", - definition: {}, - required: false, - isNew: true, - showDefaultValueField: false, - type: "string", - isInferredInputOverride: false, - }; -} - -function formInputToInputInEditing( - { key, definition, required }: BuilderFormInput, - isInferredInputOverride: boolean -): InputInEditing { - const supportedType = supportedTypes.find((type) => type === definition.type) || "unknown"; - return { - key, - definition, - required, - isNew: false, - showDefaultValueField: Boolean(definition.default), - type: supportedType !== "unknown" && definition.enum ? "enum" : supportedType, - isInferredInputOverride, - }; -} - -function inputInEditingToFormInput({ - type, - showDefaultValueField, - isNew, - ...values -}: InputInEditing): BuilderFormInput { - return { - ...values, - definition: { - ...values.definition, - type: type === "enum" ? "string" : type === "unknown" ? values.definition.type : type, - // only respect the enum values if the user explicitly selected enum as type - enum: type === "enum" && values.definition.enum?.length ? values.definition.enum : undefined, - default: showDefaultValueField ? values.definition.default : undefined, - }, - }; -} - export const InputsView: React.FC = () => { const { formatMessage } = useIntl(); - const { values, setFieldValue } = useFormikContext(); - const [inputs, , helpers] = useField("inputs"); + const { values } = useFormikContext(); const [inputInEditing, setInputInEditing] = useState(undefined); const inferredInputs = useMemo(() => getInferredInputs(values), [values]); - const usedKeys = useMemo( - () => [...inputs.value, ...inferredInputs].map((input) => input.key), - [inputs.value, inferredInputs] - ); - const inputInEditValidation = useMemo( - () => - yup.object().shape({ - // make sure key can only occur once - key: yup - .string() - .notOneOf( - inputInEditing?.isNew ? usedKeys : usedKeys.filter((key) => key !== inputInEditing?.key), - "connectorBuilder.duplicateFieldID" - ), - required: yup.bool(), - definition: yup.object().shape({ - title: yup.string().required("form.empty.error"), - }), - }), - [inputInEditing?.isNew, inputInEditing?.key, usedKeys] - ); return ( - {(inputs.value.length > 0 || inferredInputs.length > 0) && ( + {(values.inputs.length > 0 || inferredInputs.length > 0) && (
    {inferredInputs.map((input) => ( ))} - {inputs.value.map((input) => ( + {values.inputs.map((input) => ( ))}
@@ -139,190 +50,32 @@ export const InputsView: React.FC = () => { {inputInEditing && ( - { - if (values.isInferredInputOverride) { - setFieldValue(`inferredInputOverrides.${values.key}`, values.definition); - } else { - const newInput = inputInEditingToFormInput(values); - helpers.setValue( - inputInEditing.isNew - ? [...inputs.value, newInput] - : inputs.value.map((input) => (input.key === inputInEditing.key ? newInput : input)) - ); - } + { setInputInEditing(undefined); }} - > - <> - - { - helpers.setValue(inputs.value.filter((input) => input.key !== inputInEditing.key)); - setInputInEditing(undefined); - }} - onClose={() => { - setInputInEditing(undefined); - }} - /> - - + /> )}
); }; -const InputModal = ({ - inputInEditing, - onClose, - onDelete, -}: { - inputInEditing: InputInEditing; - onDelete: () => void; - onClose: () => void; -}) => { - const isInferredInputOverride = inputInEditing.isInferredInputOverride; - const { isValid, values, setFieldValue, setTouched } = useFormikContext(); - - const { formatMessage } = useIntl(); - useEffectOnce(() => { - // key input is always touched so errors are shown right away as it will be auto-set by the user changing the title - setTouched({ key: true }); - }); - - return ( - - } - onClose={onClose} - > -
- - { - if (!isInferredInputOverride) { - setFieldValue("key", sluggify(newValue || ""), true); - } - }} - label={formatMessage({ id: "connectorBuilder.inputModal.inputName" })} - tooltip={formatMessage({ id: "connectorBuilder.inputModal.inputNameTooltip" })} - /> - - - {values.type !== "unknown" && !isInferredInputOverride ? ( - <> - { - setFieldValue("definition.default", undefined); - }} - label={formatMessage({ id: "connectorBuilder.inputModal.type" })} - tooltip={formatMessage({ id: "connectorBuilder.inputModal.typeTooltip" })} - /> - {values.type === "enum" && ( - - )} - - - - {values.showDefaultValueField && ( - - )} - - - ) : ( - - {isInferredInputOverride ? ( - - ) : ( - - )} - - )} - - - {!inputInEditing.isNew && !inputInEditing.isInferredInputOverride && ( -
- -
- )} - - -
-
-
- ); -}; +function formInputToInputInEditing( + { key, definition, required }: BuilderFormInput, + isInferredInputOverride: boolean +): InputInEditing { + const supportedType = supportedTypes.find((type) => type === definition.type) || "unknown"; + return { + key, + definition, + required, + isNew: false, + showDefaultValueField: Boolean(definition.default), + type: supportedType !== "unknown" && definition.enum ? "enum" : supportedType, + isInferredInputOverride, + }; +} const InputItem = ({ input, diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.module.scss index daba1c15331c..b1480133c6eb 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.module.scss +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.module.scss @@ -21,3 +21,7 @@ $removeButtonWidth: 20px; .kvLabel { color: colors.$grey-400; } + +.inputWithHelper { + padding-right: 55px; +} diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.tsx index 14b2f5fe0817..c425c5942772 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/KeyValueListField.tsx @@ -7,6 +7,7 @@ import { Button } from "components/ui/Button"; import { Input } from "components/ui/Input"; import { Text } from "components/ui/Text"; +import { UserInputHelper } from "./BuilderFieldWithInputs"; import styles from "./KeyValueListField.module.scss"; import { RemoveButton } from "./RemoveButton"; @@ -23,13 +24,27 @@ const KeyValueInput: React.FC = ({ keyValue, onChange, onRem - onChange([e.target.value, keyValue[1]])} /> + onChange([e.target.value, keyValue[1]])} + adornment={ + onChange([newValue, keyValue[1]])} currentValue={keyValue[0]} /> + } + className={styles.inputWithHelper} + />
- onChange([keyValue[0], e.target.value])} /> + onChange([keyValue[0], e.target.value])} + adornment={ + onChange([keyValue[0], newValue])} currentValue={keyValue[1]} /> + } + className={styles.inputWithHelper} + />
diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/PaginationSection.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/PaginationSection.tsx index 0a42c59f1dac..7711ec5cd842 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/PaginationSection.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/PaginationSection.tsx @@ -10,6 +10,7 @@ import { RequestOption } from "core/request/ConnectorManifest"; import { BuilderPaginator } from "../types"; import { BuilderCard } from "./BuilderCard"; import { BuilderField } from "./BuilderField"; +import { BuilderFieldWithInputs } from "./BuilderFieldWithInputs"; import { BuilderOneOf } from "./BuilderOneOf"; import { InjectRequestOptionFields } from "./InjectRequestOptionFields"; import { ToggleGroupField } from "./ToggleGroupField"; @@ -111,13 +112,13 @@ export const PaginationSection: React.FC = ({ streamFiel typeValue: "CursorPagination", children: ( <> - - = ({ streamNum, h {selectedTab === "configuration" ? ( <> - = ({ stream label="Slice values" tooltip="List of values to iterate over" /> - = ({ stream }, children: ( <> - - - - - - = ({ stream excludeInjectIntoValues={["path"]} /> - - = ({ stream }, children: ( <> - - error?: boolean; light?: boolean; containerClassName?: string; + adornment?: ReactNode; } export const Input = React.forwardRef( - ({ light, error, containerClassName, ...props }, ref) => { + ({ light, error, containerClassName, adornment, ...props }, ref) => { const { formatMessage } = useIntl(); const inputRef = useRef(null); @@ -99,6 +100,7 @@ export const Input = React.forwardRef( props.className )} /> + {adornment} {isVisibilityButtonVisible ? (