diff --git a/backend/dataall/base/api/__init__.py b/backend/dataall/base/api/__init__.py index 4aef8e332..36f7a275a 100644 --- a/backend/dataall/base/api/__init__.py +++ b/backend/dataall/base/api/__init__.py @@ -12,6 +12,7 @@ from dataall.base.api import gql from dataall.base.api.constants import GraphQLEnumMapper +from dataall.base.api.queries import enumsQuery def bootstrap(): diff --git a/backend/dataall/base/api/queries.py b/backend/dataall/base/api/queries.py new file mode 100644 index 000000000..6401fff0b --- /dev/null +++ b/backend/dataall/base/api/queries.py @@ -0,0 +1,11 @@ +from dataall.base.api import gql +from dataall.base.api.resolvers import enum_resolver +from dataall.base.api.types import EnumResult + +enumsQuery = gql.QueryField( + name='queryEnums', + args=[gql.Argument(name='enums_names', type=gql.ArrayType(gql.String))], + type=gql.ArrayType(EnumResult), + resolver=enum_resolver, + test_scope='Enums', +) diff --git a/backend/dataall/base/api/resolvers.py b/backend/dataall/base/api/resolvers.py new file mode 100644 index 000000000..e59165d46 --- /dev/null +++ b/backend/dataall/base/api/resolvers.py @@ -0,0 +1,14 @@ +from dataall.base.api.constants import GraphQLEnumMapper + + +def enum_resolver(context, source, enums_names): + result = [] + for enum_class in GraphQLEnumMapper.__subclasses__(): + if enum_class.__name__ in enums_names: + result.append( + { + 'name': enum_class.__name__, + 'items': [{'name': item.name, 'value': str(item.value)} for item in enum_class], + } + ) + return result diff --git a/backend/dataall/base/api/types.py b/backend/dataall/base/api/types.py new file mode 100644 index 000000000..8d0236d64 --- /dev/null +++ b/backend/dataall/base/api/types.py @@ -0,0 +1,17 @@ +from dataall.base.api import gql + +EnumItem = gql.ObjectType( + name='EnumItem', + fields=[ + gql.Field(name='name', type=gql.String), + gql.Field(name='value', type=gql.String), + ], +) + +EnumResult = gql.ObjectType( + name='EnumResult', + fields=[ + gql.Field(name='name', type=gql.String), + gql.Field(name='items', type=gql.ArrayType(EnumItem)), + ], +) diff --git a/backend/dataall/base/cdkproxy/requirements.txt b/backend/dataall/base/cdkproxy/requirements.txt index f55e149a6..2c9be32f7 100644 --- a/backend/dataall/base/cdkproxy/requirements.txt +++ b/backend/dataall/base/cdkproxy/requirements.txt @@ -1,7 +1,7 @@ aws-cdk-lib==2.99.0 -boto3==1.28.23 -boto3-stubs==1.28.23 -botocore==1.31.23 +boto3==1.34.119 +boto3-stubs==1.34.119 +botocore==1.34.119 cdk-nag==2.7.2 constructs==10.0.73 starlette==0.36.3 diff --git a/backend/dataall/base/db/paginator.py b/backend/dataall/base/db/paginator.py index 6d2657580..3b502f36a 100644 --- a/backend/dataall/base/db/paginator.py +++ b/backend/dataall/base/db/paginator.py @@ -44,3 +44,14 @@ def paginate(query, page, page_size): # nosemgrep: python.sqlalchemy.performance.performance-improvements.len-all-count total = len(query.order_by(None).all()) return Page(items, page, page_size, total) + + +def paginate_list(items, page, page_size): + if page <= 0: + raise AttributeError('page needs to be >= 1') + if page_size <= 0: + raise AttributeError('page_size needs to be >= 1') + start = (page - 1) * page_size + end = start + page_size + total = len(items) + return Page(items[start:end], page, page_size, total) diff --git a/backend/dataall/core/environment/cdk/pivot_role_stack.py b/backend/dataall/core/environment/cdk/pivot_role_stack.py index 8228c58fd..d6368ea55 100644 --- a/backend/dataall/core/environment/cdk/pivot_role_stack.py +++ b/backend/dataall/core/environment/cdk/pivot_role_stack.py @@ -32,8 +32,8 @@ def generate_policies(self) -> List[iam.ManagedPolicy]: ) for service in services: - statements.extend(service.get_statements(self)) logger.info(f'Adding {service.__name__} statements to policy') + statements.extend(service.get_statements(self)) logger.info(f'statements: {str(service.get_statements(self))}') statements_chunks = split_policy_statements_in_chunks(statements) diff --git a/backend/dataall/core/permissions/db/resource_policy/resource_policy_repositories.py b/backend/dataall/core/permissions/db/resource_policy/resource_policy_repositories.py index ecb68ce65..4b2b4d6c9 100644 --- a/backend/dataall/core/permissions/db/resource_policy/resource_policy_repositories.py +++ b/backend/dataall/core/permissions/db/resource_policy/resource_policy_repositories.py @@ -70,6 +70,7 @@ def has_group_resource_permission( else: return policy + @staticmethod def query_all_resource_policies( session, group_uri: str, resource_uri: str, resource_type: str = None, permissions: List[str] = None ): diff --git a/backend/dataall/modules/datasets_base/services/datasets_enums.py b/backend/dataall/modules/datasets_base/services/datasets_enums.py index df5db44ac..49a252456 100644 --- a/backend/dataall/modules/datasets_base/services/datasets_enums.py +++ b/backend/dataall/modules/datasets_base/services/datasets_enums.py @@ -7,6 +7,7 @@ class DatasetTypes(GraphQLEnumMapper): S3 = 'S3' + Redshift = 'Redshift' class DatasetRole(GraphQLEnumMapper): diff --git a/backend/dataall/modules/redshift_datasets/__init__.py b/backend/dataall/modules/redshift_datasets/__init__.py new file mode 100644 index 000000000..cd9e73f68 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/__init__.py @@ -0,0 +1,96 @@ +"""Contains the code related to datasets""" + +import logging +from typing import List, Type, Set + +from dataall.base.loader import ModuleInterface, ImportMode + +log = logging.getLogger(__name__) + + +class RedshiftDatasetApiModuleInterface(ModuleInterface): + """Implements ModuleInterface for dataset GraphQl lambda""" + + @staticmethod + def is_supported(modes): + return ImportMode.API in modes + + @staticmethod + def depends_on() -> List[Type['ModuleInterface']]: + from dataall.modules.datasets_base import DatasetBaseApiModuleInterface + from dataall.modules.catalog import CatalogApiModuleInterface + from dataall.modules.feed import FeedApiModuleInterface + from dataall.modules.vote import VoteApiModuleInterface + + return [ + DatasetBaseApiModuleInterface, + CatalogApiModuleInterface, + FeedApiModuleInterface, + VoteApiModuleInterface, + ] + + def __init__(self): + from dataall.modules.vote.services.vote_service import add_vote_type + from dataall.modules.feed.api.registry import FeedRegistry, FeedDefinition + from dataall.modules.catalog.indexers.registry import GlossaryRegistry, GlossaryDefinition + from dataall.core.environment.services.environment_resource_manager import EnvironmentResourceManager + + from dataall.modules.redshift_datasets.indexers.dataset_indexer import DatasetIndexer + from dataall.modules.redshift_datasets.indexers.table_indexer import DatasetTableIndexer + from dataall.modules.redshift_datasets.db.redshift_dataset_repositories import ( + RedshiftDatasetEnvironmentResource, + ) + from dataall.modules.redshift_datasets.db.redshift_connection_repositories import ( + RedshiftConnectionEnvironmentResource, + ) + from dataall.modules.redshift_datasets.db.redshift_models import RedshiftDataset, RedshiftTable + from dataall.modules.redshift_datasets.services.redshift_constants import ( + GLOSSARY_REDSHIFT_DATASET_NAME, + GLOSSARY_REDSHIFT_DATASET_TABLE_NAME, + FEED_REDSHIFT_DATASET_NAME, + FEED_REDSHIFT_DATASET_TABLE_NAME, + VOTE_REDSHIFT_DATASET_NAME, + ) + + import dataall.modules.redshift_datasets.api + + FeedRegistry.register(FeedDefinition(FEED_REDSHIFT_DATASET_TABLE_NAME, RedshiftTable)) + FeedRegistry.register(FeedDefinition(FEED_REDSHIFT_DATASET_NAME, RedshiftDataset)) + + GlossaryRegistry.register( + GlossaryDefinition( + target_type=GLOSSARY_REDSHIFT_DATASET_NAME, + object_type='RedshiftDataset', + model=RedshiftDataset, + reindexer=DatasetIndexer, + ) + ) + + GlossaryRegistry.register( + GlossaryDefinition( + target_type=GLOSSARY_REDSHIFT_DATASET_TABLE_NAME, + object_type='RedshiftDatasetTable', + model=RedshiftTable, + reindexer=DatasetTableIndexer, + ) + ) + + add_vote_type(VOTE_REDSHIFT_DATASET_NAME, DatasetIndexer) + + EnvironmentResourceManager.register(RedshiftDatasetEnvironmentResource()) + EnvironmentResourceManager.register(RedshiftConnectionEnvironmentResource()) + + log.info('API of Redshift datasets has been imported') + + +class RedshiftDatasetCdkModuleInterface(ModuleInterface): + """Loads dataset cdk stacks""" + + @staticmethod + def is_supported(modes: Set[ImportMode]): + return ImportMode.CDK in modes + + def __init__(self): + import dataall.modules.redshift_datasets.cdk + + log.info('Redshift Dataset CDK has been imported') diff --git a/backend/dataall/modules/redshift_datasets/api/__init__.py b/backend/dataall/modules/redshift_datasets/api/__init__.py new file mode 100644 index 000000000..b375b5a66 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/__init__.py @@ -0,0 +1,5 @@ +"""The GraphQL schema of datasets and related functionality""" + +from dataall.modules.redshift_datasets.api import connections, datasets + +__all__ = ['connections', 'datasets'] diff --git a/backend/dataall/modules/redshift_datasets/api/connections/__init__.py b/backend/dataall/modules/redshift_datasets/api/connections/__init__.py new file mode 100644 index 000000000..d72a971ec --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/connections/__init__.py @@ -0,0 +1,9 @@ +from . import ( + input_types, + queries, + mutations, + resolvers, + types, +) + +__all__ = ['resolvers', 'types', 'input_types', 'queries', 'mutations'] diff --git a/backend/dataall/modules/redshift_datasets/api/connections/enums.py b/backend/dataall/modules/redshift_datasets/api/connections/enums.py new file mode 100644 index 000000000..de49eeca6 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/connections/enums.py @@ -0,0 +1,6 @@ +from dataall.base.api.constants import GraphQLEnumMapper + + +class RedshiftType(GraphQLEnumMapper): + Serverless = 'serverless' + Cluster = 'cluster' diff --git a/backend/dataall/modules/redshift_datasets/api/connections/input_types.py b/backend/dataall/modules/redshift_datasets/api/connections/input_types.py new file mode 100644 index 000000000..d742324c8 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/connections/input_types.py @@ -0,0 +1,30 @@ +from dataall.base.api import gql + + +CreateRedshiftConnectionInput = gql.InputType( + name='CreateRedshiftConnectionInput', + arguments=[ + gql.Argument('connectionName', gql.NonNullableType(gql.String)), + gql.Argument('environmentUri', gql.NonNullableType(gql.String)), + gql.Argument('SamlGroupName', gql.NonNullableType(gql.String)), + gql.Argument('redshiftType', gql.NonNullableType(gql.String)), + gql.Argument('clusterId', gql.String), + gql.Argument('nameSpaceId', gql.String), + gql.Argument('workgroup', gql.String), + gql.Argument('database', gql.NonNullableType(gql.String)), + gql.Argument('redshiftUser', gql.String), + gql.Argument('secretArn', gql.String), + ], +) + + +ConnectionFilter = gql.InputType( + name='ConnectionFilter', + arguments=[ + gql.Argument('term', gql.String), + gql.Argument('page', gql.Integer), + gql.Argument('pageSize', gql.Integer), + gql.Argument('environmentUri', gql.String), + gql.Argument('groupUri', gql.String), + ], +) diff --git a/backend/dataall/modules/redshift_datasets/api/connections/mutations.py b/backend/dataall/modules/redshift_datasets/api/connections/mutations.py new file mode 100644 index 000000000..291944e33 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/connections/mutations.py @@ -0,0 +1,22 @@ +from dataall.base.api import gql +from dataall.modules.redshift_datasets.api.connections.resolvers import ( + create_redshift_connection, + delete_redshift_connection, +) +from dataall.modules.redshift_datasets.api.connections.types import ( + RedshiftConnection, +) + +createRedshiftConnection = gql.MutationField( + name='createRedshiftConnection', + args=[gql.Argument('input', gql.Ref('CreateRedshiftConnectionInput'))], + type=RedshiftConnection, + resolver=create_redshift_connection, +) + +deleteRedshiftConnection = gql.MutationField( + name='deleteRedshiftConnection', + args=[gql.Argument('connectionUri', gql.NonNullableType(gql.String))], + type=gql.Boolean, + resolver=delete_redshift_connection, +) diff --git a/backend/dataall/modules/redshift_datasets/api/connections/queries.py b/backend/dataall/modules/redshift_datasets/api/connections/queries.py new file mode 100644 index 000000000..166ee5a7b --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/connections/queries.py @@ -0,0 +1,30 @@ +from dataall.base.api import gql +from dataall.modules.redshift_datasets.api.connections.resolvers import ( + list_environment_redshift_connections, + list_redshift_connection_schemas, + list_redshift_schema_tables, +) + +listEnvironmentRedshiftConnections = gql.QueryField( + name='listEnvironmentRedshiftConnections', + args=[gql.Argument('filter', gql.Ref('ConnectionFilter'))], + type=gql.Ref('RedshiftConnectionSearchResult'), + resolver=list_environment_redshift_connections, +) + +listRedshiftConnectionSchemas = gql.QueryField( + name='listRedshiftConnectionSchemas', + args=[gql.Argument('connectionUri', gql.NonNullableType(gql.String))], + type=gql.ArrayType(gql.String), + resolver=list_redshift_connection_schemas, +) + +listRedshiftSchemaTables = gql.QueryField( + name='listRedshiftSchemaTables', + args=[ + gql.Argument('connectionUri', gql.NonNullableType(gql.String)), + gql.Argument('schema', gql.NonNullableType(gql.String)), + ], + type=gql.ArrayType(gql.Ref('RedshiftTable')), + resolver=list_redshift_schema_tables, +) diff --git a/backend/dataall/modules/redshift_datasets/api/connections/resolvers.py b/backend/dataall/modules/redshift_datasets/api/connections/resolvers.py new file mode 100644 index 000000000..95d700ef0 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/connections/resolvers.py @@ -0,0 +1,61 @@ +import logging +from typing import Any +from dataall.base.db import exceptions +from dataall.base.api.context import Context + +from dataall.modules.redshift_datasets.api.connections.enums import RedshiftType +from dataall.modules.redshift_datasets.services.redshift_connection_service import RedshiftConnectionService + +log = logging.getLogger(__name__) + + +def create_redshift_connection(context: Context, source, input=None): + RequestValidator.validate_connection_creation_request(data=input) + return RedshiftConnectionService.create_redshift_connection( + uri=input['environmentUri'], admin_group=input['SamlGroupName'], data=input + ) + + +def delete_redshift_connection(context: Context, source, connectionUri: str): + RequestValidator.required_param('connectionUri', connectionUri) + return RedshiftConnectionService.delete_redshift_connection(uri=connectionUri) + + +def list_environment_redshift_connections(context: Context, source, filter: dict = None): + environmentUri = filter['environmentUri'] + RequestValidator.required_param('environmentUri', environmentUri) + return RedshiftConnectionService.list_environment_redshift_connections(uri=environmentUri, filter=filter) + + +def list_redshift_connection_schemas(context: Context, source, connectionUri): + RequestValidator.required_param('connectionUri', connectionUri) + return RedshiftConnectionService.list_connection_schemas(uri=connectionUri) + + +def list_redshift_schema_tables(context: Context, source, connectionUri: str, schema: str): + RequestValidator.required_param('connectionUri', connectionUri) + RequestValidator.required_param('schema', schema) + return RedshiftConnectionService.list_schema_tables(uri=connectionUri, schema=schema) + + +class RequestValidator: + def required_param(param_name: str, param_value: Any): + if not param_value: + raise exceptions.RequiredParameter(param_name) + + def validate_connection_creation_request(data): + if not data: + raise exceptions.RequiredParameter('data') + + RequestValidator.required_param('SamlGroupName', data.get('SamlGroupName')) + RequestValidator.required_param('environmentUri', data.get('environmentUri')) + RequestValidator.required_param('connectionName', data.get('connectionName')) + RequestValidator.required_param('redshiftType', data.get('redshiftType')) + RequestValidator.required_param('database', data.get('database')) + if not data.get('redshiftUser') and not data.get('secretArn'): + raise exceptions.RequiredParameter('RedshiftUser OR secretArn') + if data.get('redshiftType') == RedshiftType.Serverless.value: + RequestValidator.required_param('nameSpaceId', data.get('nameSpaceId')) + RequestValidator.required_param('workgroup', data.get('workgroup')) + if data.get('redshiftType') == RedshiftType.Cluster.value: + RequestValidator.required_param('clusterId', data.get('clusterId')) diff --git a/backend/dataall/modules/redshift_datasets/api/connections/types.py b/backend/dataall/modules/redshift_datasets/api/connections/types.py new file mode 100644 index 000000000..54a6d3968 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/connections/types.py @@ -0,0 +1,40 @@ +from dataall.base.api import gql + +RedshiftConnection = gql.ObjectType( + name='RedshiftConnection', + fields=[ + gql.Field('connectionUri', gql.ID), + gql.Field('name', gql.String), + gql.Field('connectionType', gql.String), + gql.Field('SamlGroupName', gql.String), + gql.Field('label', gql.String), + gql.Field('redshiftType', gql.String), + gql.Field('clusterId', gql.String), + gql.Field('nameSpaceId', gql.String), + gql.Field('workgroup', gql.String), + gql.Field('database', gql.String), + gql.Field('redshiftUser', gql.String), + gql.Field('secretArn', gql.String), + ], +) + +RedshiftConnectionSearchResult = gql.ObjectType( + name='RedshiftConnectionSearchResult', + fields=[ + gql.Field('count', gql.Integer), + gql.Field('page', gql.Integer), + gql.Field('pages', gql.Integer), + gql.Field('hasNext', gql.Boolean), + gql.Field('hasPrevious', gql.Boolean), + gql.Field('nodes', gql.ArrayType(RedshiftConnection)), + ], +) + +RedshiftTable = gql.ObjectType( + name='RedshiftTable', + fields=[ + gql.Field('name', gql.String), + gql.Field('type', gql.String), + gql.Field('alreadyAdded', gql.String), + ], +) diff --git a/backend/dataall/modules/redshift_datasets/api/datasets/__init__.py b/backend/dataall/modules/redshift_datasets/api/datasets/__init__.py new file mode 100644 index 000000000..d72a971ec --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/datasets/__init__.py @@ -0,0 +1,9 @@ +from . import ( + input_types, + queries, + mutations, + resolvers, + types, +) + +__all__ = ['resolvers', 'types', 'input_types', 'queries', 'mutations'] diff --git a/backend/dataall/modules/redshift_datasets/api/datasets/enums.py b/backend/dataall/modules/redshift_datasets/api/datasets/enums.py new file mode 100644 index 000000000..8d1048a7f --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/datasets/enums.py @@ -0,0 +1 @@ +"""Contains the enums GraphQL mapping for enums""" diff --git a/backend/dataall/modules/redshift_datasets/api/datasets/input_types.py b/backend/dataall/modules/redshift_datasets/api/datasets/input_types.py new file mode 100644 index 000000000..dfdb6360d --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/datasets/input_types.py @@ -0,0 +1,46 @@ +from dataall.base.api import gql + + +ImportRedshiftDatasetInput = gql.InputType( + name='ImportRedshiftDatasetInput', + arguments=[ + gql.Argument('label', gql.NonNullableType(gql.String)), + gql.Argument('organizationUri', gql.NonNullableType(gql.String)), + gql.Argument('environmentUri', gql.NonNullableType(gql.String)), + gql.Argument('description', gql.String), + gql.Argument('tags', gql.ArrayType(gql.String)), + gql.Argument('owner', gql.NonNullableType(gql.String)), + gql.Argument('topics', gql.ArrayType(gql.Ref('Topic'))), + gql.Argument('SamlAdminGroupName', gql.NonNullableType(gql.String)), + gql.Argument('confidentiality', gql.String), + gql.Argument('stewards', gql.String), + gql.Argument('autoApprovalEnabled', gql.Boolean), + gql.Argument('connectionUri', gql.NonNullableType(gql.String)), + gql.Argument('schema', gql.NonNullableType(gql.String)), + gql.Argument('tables', gql.ArrayType(gql.String)), + ], +) + + +ModifyRedshiftDatasetInput = gql.InputType( + name='ModifyRedshiftDatasetInput', + arguments=[ + gql.Argument('label', gql.String), + gql.Argument('description', gql.String), + gql.Argument('tags', gql.ArrayType(gql.String)), + gql.Argument('topics', gql.ArrayType(gql.Ref('Topic'))), + gql.Argument('terms', gql.ArrayType(gql.String)), + gql.Argument('confidentiality', gql.String), + gql.Argument('stewards', gql.String), + gql.Argument('autoApprovalEnabled', gql.Boolean), + ], +) + +RedshiftDatasetTableFilter = gql.InputType( + name='RedshiftDatasetTableFilter', + arguments=[ + gql.Argument('term', gql.String), + gql.Argument('page', gql.Integer), + gql.Argument('pageSize', gql.Integer), + ], +) diff --git a/backend/dataall/modules/redshift_datasets/api/datasets/mutations.py b/backend/dataall/modules/redshift_datasets/api/datasets/mutations.py new file mode 100644 index 000000000..330883b32 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/datasets/mutations.py @@ -0,0 +1,68 @@ +from dataall.base.api import gql +from dataall.modules.redshift_datasets.api.datasets.input_types import ( + ImportRedshiftDatasetInput, + ModifyRedshiftDatasetInput, +) +from dataall.modules.redshift_datasets.api.datasets.resolvers import ( + add_redshift_dataset_tables, + delete_redshift_dataset, + delete_redshift_dataset_table, + import_redshift_dataset, + update_redshift_dataset, + update_redshift_dataset_table, +) + +importRedshiftDataset = gql.MutationField( + name='importRedshiftDataset', + args=[gql.Argument('input', ImportRedshiftDatasetInput)], + type=gql.Ref('RedshiftDataset'), + resolver=import_redshift_dataset, +) + +updateRedshiftDataset = gql.MutationField( + name='updateRedshiftDataset', + args=[ + gql.Argument('datasetUri', gql.String), + gql.Argument('input', ModifyRedshiftDatasetInput), + ], + type=gql.Ref('RedshiftDataset'), + resolver=update_redshift_dataset, +) + +deleteRedshiftDataset = gql.MutationField( + name='deleteRedshiftDataset', + args=[ + gql.Argument('datasetUri', gql.NonNullableType(gql.String)), + ], + resolver=delete_redshift_dataset, + type=gql.Boolean, +) + +addRedshiftDatasetTables = gql.MutationField( + name='addRedshiftDatasetTables', + args=[ + gql.Argument('datasetUri', gql.NonNullableType(gql.String)), + gql.Argument('tables', gql.NonNullableType(gql.ArrayType(gql.String))), + ], + type=gql.Boolean, + resolver=add_redshift_dataset_tables, +) + +deleteRedshiftDatasetTable = gql.MutationField( + name='deleteRedshiftDatasetTable', + args=[ + gql.Argument('rsTableUri', gql.NonNullableType(gql.String)), + ], + type=gql.Boolean, + resolver=delete_redshift_dataset_table, +) + +updateRedshiftDatasetTable = gql.MutationField( + name='updateRedshiftDatasetTable', + args=[ + gql.Argument('rsTableUri', gql.String), + gql.Argument('input', ModifyRedshiftDatasetInput), + ], + type=gql.Ref('RedshiftDatasetTable'), + resolver=update_redshift_dataset_table, +) diff --git a/backend/dataall/modules/redshift_datasets/api/datasets/queries.py b/backend/dataall/modules/redshift_datasets/api/datasets/queries.py new file mode 100644 index 000000000..2da615b60 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/datasets/queries.py @@ -0,0 +1,53 @@ +from dataall.base.api import gql +from dataall.modules.redshift_datasets.api.datasets.resolvers import ( + get_redshift_dataset, + get_redshift_dataset_table, + list_redshift_schema_dataset_tables, + list_redshift_dataset_table_columns, + list_redshift_dataset_tables, +) +from dataall.modules.redshift_datasets.api.datasets.input_types import RedshiftDatasetTableFilter + + +getRedshiftDataset = gql.QueryField( + name='getRedshiftDataset', + args=[gql.Argument('datasetUri', gql.NonNullableType(gql.String))], + type=gql.Ref('RedshiftDataset'), + resolver=get_redshift_dataset, +) + +listRedshiftDatasetTables = gql.QueryField( + name='listRedshiftDatasetTables', + args=[ + gql.Argument('datasetUri', gql.NonNullableType(gql.String)), + gql.Argument('filter', RedshiftDatasetTableFilter), + ], + type=gql.Ref('RedshiftDatasetTableSearchResult'), + resolver=list_redshift_dataset_tables, +) + +getRedshiftDatasetTable = gql.QueryField( + name='getRedshiftDatasetTable', + args=[gql.Argument('rsTableUri', gql.NonNullableType(gql.String))], + type=gql.Ref('RedshiftDatasetTable'), + resolver=get_redshift_dataset_table, +) + +getRedshiftDatasetTableColumns = gql.QueryField( + name='getRedshiftDatasetTableColumns', + args=[ + gql.Argument('rsTableUri', gql.NonNullableType(gql.String)), + gql.Argument('filter', RedshiftDatasetTableFilter), + ], + type=gql.Ref('RedshiftDatasetTableColumnSearchResult'), + resolver=list_redshift_dataset_table_columns, +) + +listRedshiftSchemaDatasetTables = gql.QueryField( + name='listRedshiftSchemaDatasetTables', + args=[ + gql.Argument('datasetUri', gql.NonNullableType(gql.String)), + ], + type=gql.ArrayType(gql.Ref('RedshiftTable')), + resolver=list_redshift_schema_dataset_tables, +) diff --git a/backend/dataall/modules/redshift_datasets/api/datasets/resolvers.py b/backend/dataall/modules/redshift_datasets/api/datasets/resolvers.py new file mode 100644 index 000000000..6579099b9 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/datasets/resolvers.py @@ -0,0 +1,162 @@ +import logging +from typing import Any +from dataall.base.api.context import Context +from dataall.base.db import exceptions +from dataall.core.environment.services.environment_service import EnvironmentService +from dataall.core.organizations.db.organization_repositories import OrganizationRepository +from dataall.modules.catalog.db.glossary_repositories import GlossaryRepository +from dataall.modules.datasets_base.services.datasets_enums import DatasetRole +from dataall.modules.redshift_datasets.db.redshift_models import RedshiftDataset, RedshiftTable +from dataall.modules.redshift_datasets.services.redshift_dataset_service import RedshiftDatasetService +from dataall.modules.redshift_datasets.services.redshift_connection_service import RedshiftConnectionService +from dataall.modules.redshift_datasets.services.redshift_constants import ( + GLOSSARY_REDSHIFT_DATASET_NAME, + GLOSSARY_REDSHIFT_DATASET_TABLE_NAME, +) + +log = logging.getLogger(__name__) + + +def import_redshift_dataset(context: Context, source, input=None): + RequestValidator.validate_dataset_import_request(input) + admin_group = input['SamlAdminGroupName'] + uri = input['environmentUri'] + return RedshiftDatasetService.import_redshift_dataset(uri=uri, admin_group=admin_group, data=input) + + +def update_redshift_dataset(context: Context, source, datasetUri: str, input: dict): + RequestValidator.required_param('datasetUri', datasetUri) + return RedshiftDatasetService.update_redshift_dataset(uri=datasetUri, data=input) + + +def delete_redshift_dataset(context: Context, source, datasetUri: str): + RequestValidator.required_param('datasetUri', datasetUri) + return RedshiftDatasetService.delete_redshift_dataset(uri=datasetUri) + + +def list_redshift_schema_dataset_tables(context: Context, source, datasetUri: str): + RequestValidator.required_param('datasetUri', datasetUri) + return RedshiftDatasetService.list_redshift_schema_dataset_tables(uri=datasetUri) + + +def add_redshift_dataset_tables(context: Context, source, datasetUri: str, tables: [str]): + RequestValidator.required_param('datasetUri', datasetUri) + RequestValidator.required_param('tables', tables) + return RedshiftDatasetService.add_redshift_dataset_tables(uri=datasetUri, tables=tables) + + +def delete_redshift_dataset_table(context: Context, source, rsTableUri: str): + RequestValidator.required_param('rsTableUri', rsTableUri) + return RedshiftDatasetService.delete_redshift_dataset_table(uri=rsTableUri) + + +def update_redshift_dataset_table(context: Context, source, rsTableUri: str, input: dict): + RequestValidator.required_param('rsTableUri', rsTableUri) + return RedshiftDatasetService.update_redshift_dataset_table(uri=rsTableUri, data=input) + + +def get_redshift_dataset(context, source, datasetUri: str): + RequestValidator.required_param('datasetUri', datasetUri) + return RedshiftDatasetService.get_redshift_dataset(uri=datasetUri) + + +def list_redshift_dataset_tables(context, source, datasetUri: str, filter: dict = None): + RequestValidator.required_param('datasetUri', datasetUri) + return RedshiftDatasetService.list_redshift_dataset_tables(uri=datasetUri, filter=filter) + + +def get_redshift_dataset_table(context, source, rsTableUri: str): + RequestValidator.required_param('rsTableUri', rsTableUri) + return RedshiftDatasetService.get_redshift_dataset_table(uri=rsTableUri) + + +def list_redshift_dataset_table_columns(context, source, rsTableUri: str, filter: dict = None): + RequestValidator.required_param('rsTableUri', rsTableUri) + return RedshiftDatasetService.list_redshift_dataset_table_columns(uri=rsTableUri, filter=filter) + + +def resolve_dataset_organization(context, source: RedshiftDataset, **kwargs): + if not source: + return None + with context.engine.scoped_session() as session: + return OrganizationRepository.get_organization_by_uri(session, source.organizationUri) + + +def resolve_dataset_environment( + context, source: RedshiftDataset, **kwargs +): # TODO- duplicated with S3 datasets - follow-up PR + if not source: + return None + with context.engine.scoped_session() as session: + return EnvironmentService.get_environment_by_uri(session, source.environmentUri) + + +def resolve_dataset_owners_group( + context, source: RedshiftDataset, **kwargs +): # TODO- duplicated with S3 datasets - follow-up PR + if not source: + return None + return source.SamlAdminGroupName + + +def resolve_dataset_stewards_group( + context, source: RedshiftDataset, **kwargs +): # TODO- duplicated with S3 datasets - follow-up PR + if not source: + return None + return source.stewards + + +def resolve_user_role(context: Context, source: RedshiftDataset, **kwargs): + if not source: + return None + if source.owner == context.username: + return DatasetRole.Creator.value + elif source.SamlAdminGroupName in context.groups: + return DatasetRole.Admin.value + elif source.stewards in context.groups: + return DatasetRole.DataSteward.value + return DatasetRole.NoPermission.value + + +def resolve_dataset_glossary_terms(context: Context, source: RedshiftDataset, **kwargs): + if not source: + return None + with context.engine.scoped_session() as session: + return GlossaryRepository.get_glossary_terms_links(session, source.datasetUri, GLOSSARY_REDSHIFT_DATASET_NAME) + + +def resolve_table_glossary_terms(context: Context, source: RedshiftTable, **kwargs): + if not source: + return None + with context.engine.scoped_session() as session: + return GlossaryRepository.get_glossary_terms_links( + session, source.rsTableUri, GLOSSARY_REDSHIFT_DATASET_TABLE_NAME + ) + + +def resolve_dataset_connection(context: Context, source: RedshiftDataset, **kwargs): + return RedshiftConnectionService.get_redshift_connection_by_uri(uri=source.connectionUri) + + +def resolve_dataset_upvotes(context: Context, source: RedshiftDataset, **kwargs): + return RedshiftDatasetService.get_dataset_upvotes(uri=source.datasetUri) + + +def resolve_table_dataset(context: Context, source: RedshiftTable, **kwargs): + return RedshiftDatasetService.get_redshift_dataset(uri=source.datasetUri) + + +class RequestValidator: + @staticmethod + def required_param(param_name: str, param_value: Any): + if not param_value: + raise exceptions.RequiredParameter(param_name) + + @staticmethod + def validate_dataset_import_request(data): + RequestValidator.required_param('input', data) + RequestValidator.required_param('label', data.get('label')) + RequestValidator.required_param('SamlAdminGroupName', data.get('SamlAdminGroupName')) + RequestValidator.required_param('connectionUri', data.get('connectionUri')) + RequestValidator.required_param('schema', data.get('schema')) diff --git a/backend/dataall/modules/redshift_datasets/api/datasets/types.py b/backend/dataall/modules/redshift_datasets/api/datasets/types.py new file mode 100644 index 000000000..ab1fd1b52 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/api/datasets/types.py @@ -0,0 +1,153 @@ +from dataall.base.api import gql +from dataall.modules.datasets_base.services.datasets_enums import DatasetRole + +from dataall.modules.redshift_datasets.api.datasets.resolvers import ( + resolve_dataset_environment, + resolve_dataset_organization, + resolve_dataset_owners_group, + resolve_dataset_stewards_group, + resolve_user_role, + resolve_dataset_glossary_terms, + resolve_table_glossary_terms, + resolve_dataset_connection, + resolve_dataset_upvotes, + resolve_table_dataset, +) + +RedshiftDataset = gql.ObjectType( + name='RedshiftDataset', + fields=[ + gql.Field('datasetUri', gql.ID), + gql.Field('label', gql.String), + gql.Field('name', gql.String), + gql.Field('description', gql.String), + gql.Field('tags', gql.ArrayType(gql.String)), + gql.Field('owner', gql.String), + gql.Field('created', gql.String), + gql.Field('updated', gql.String), + gql.Field('admins', gql.ArrayType(gql.String)), + gql.Field('AwsAccountId', gql.String), + gql.Field('region', gql.String), + gql.Field('SamlAdminGroupName', gql.String), + gql.Field('imported', gql.Boolean), + gql.Field( + name='environment', + type=gql.Ref('Environment'), + resolver=resolve_dataset_environment, + ), + gql.Field( + name='organization', + type=gql.Ref('Organization'), + resolver=resolve_dataset_organization, + ), + gql.Field( + name='owners', + type=gql.String, + resolver=resolve_dataset_owners_group, + ), + gql.Field( + name='stewards', + type=gql.String, + resolver=resolve_dataset_stewards_group, + ), + gql.Field( + name='userRoleForDataset', + type=DatasetRole.toGraphQLEnum(), + resolver=resolve_user_role, + ), + gql.Field( + name='terms', + resolver=resolve_dataset_glossary_terms, + type=gql.Ref('TermSearchResult'), + ), + gql.Field('topics', gql.ArrayType(gql.Ref('Topic'))), + gql.Field('confidentiality', gql.String), + gql.Field('autoApprovalEnabled', gql.Boolean), + gql.Field('schema', gql.String), + gql.Field('upvotes', gql.Integer, resolver=resolve_dataset_upvotes), + gql.Field( + name='connection', + type=gql.Ref('RedshiftConnection'), + resolver=resolve_dataset_connection, + ), + ], +) + +RedshiftDatasetTable = gql.ObjectType( + name='RedshiftDatasetTable', + fields=[ + gql.Field('rsTableUri', gql.ID), + gql.Field('datasetUri', gql.String), + gql.Field('label', gql.String), + gql.Field('name', gql.String), + gql.Field('description', gql.String), + gql.Field('owner', gql.String), + gql.Field('created', gql.String), + gql.Field('updated', gql.String), + gql.Field('region', gql.String), + gql.Field('tags', gql.ArrayType(gql.String)), + gql.Field( + name='terms', + resolver=resolve_table_glossary_terms, + type=gql.Ref('TermSearchResult'), + ), + gql.Field('dataset', gql.Ref('RedshiftDataset'), resolver=resolve_table_dataset), + ], +) + +RedshiftDatasetTableListItem = gql.ObjectType( + name='RedshiftDatasetTableListItem', + fields=[ + gql.Field('rsTableUri', gql.ID), + gql.Field('datasetUri', gql.String), + gql.Field('label', gql.String), + gql.Field('name', gql.String), + gql.Field('description', gql.String), + gql.Field('owner', gql.String), + gql.Field('created', gql.String), + gql.Field('updated', gql.String), + gql.Field('region', gql.String), + gql.Field('tags', gql.ArrayType(gql.String)), + ], +) + +RedshiftDatasetTableSearchResult = gql.ObjectType( + name='RedshiftDatasetTableSearchResult', + fields=[ + gql.Field('nodes', gql.ArrayType(RedshiftDatasetTableListItem)), + gql.Field('count', gql.Integer), + gql.Field('pages', gql.Integer), + gql.Field('page', gql.Integer), + gql.Field('hasNext', gql.Boolean), + gql.Field('hasPrevious', gql.Boolean), + ], +) + +RedshiftDatasetTableColumn = gql.ObjectType( + name='RedshiftDatasetTableColumn', + fields=[ + gql.Field('columnDefault', gql.String), + gql.Field('isCaseSensitive', gql.Boolean), + gql.Field('isCurrency', gql.Boolean), + gql.Field('isSigned', gql.Boolean), + gql.Field('label', gql.String), + gql.Field('length', gql.Integer), + gql.Field('name', gql.String), + gql.Field('nullable', gql.Boolean), + gql.Field('precision', gql.Integer), + gql.Field('scale', gql.Integer), + gql.Field('typeName', gql.String), + ], +) + +RedshiftDatasetTableColumnSearchResult = gql.ObjectType( + name='RedshiftDatasetTableColumnSearchResult', + fields=[ + gql.Field('nodes', gql.ArrayType(RedshiftDatasetTableColumn)), + gql.Field('count', gql.Integer), + gql.Field('pages', gql.Integer), + gql.Field('page', gql.Integer), + gql.Field('hasNext', gql.Boolean), + gql.Field('hasPrevious', gql.Boolean), + ], +) diff --git a/backend/dataall/modules/redshift_datasets/aws/__init__.py b/backend/dataall/modules/redshift_datasets/aws/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/dataall/modules/redshift_datasets/aws/redshift.py b/backend/dataall/modules/redshift_datasets/aws/redshift.py new file mode 100644 index 000000000..2414643be --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/aws/redshift.py @@ -0,0 +1,21 @@ +import logging + +from botocore.exceptions import ClientError + +from dataall.base.aws.sts import SessionHelper + +log = logging.getLogger(__name__) + + +class RedshiftClient: + def __init__(self, account_id: str, region: str) -> None: + session = SessionHelper.remote_session(accountid=account_id, region=region) + self.client = session.client(service_name='redshift', region_name=region) + + def describe_cluster(self, clusterId: str): + try: + log.info(f'Describing cluster {clusterId=}') + return self.client.describe_clusters(ClusterIdentifier=clusterId)['Clusters'][0] + except ClientError as e: + log.error(e) + raise e diff --git a/backend/dataall/modules/redshift_datasets/aws/redshift_data.py b/backend/dataall/modules/redshift_datasets/aws/redshift_data.py new file mode 100644 index 000000000..c821cd553 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/aws/redshift_data.py @@ -0,0 +1,145 @@ +import logging +import time +from botocore.exceptions import ClientError +from dataall.base.aws.sts import SessionHelper +from dataall.modules.redshift_datasets.db.redshift_models import RedshiftConnection + +log = logging.getLogger(__name__) + + +class RedshiftDataClient: + def __init__(self, account_id: str, region: str, connection: RedshiftConnection) -> None: + session = SessionHelper.remote_session(accountid=account_id, region=region) + self.client = session.client(service_name='redshift-data', region_name=region) + self.database = connection.database + self.execute_connection_params = { + 'Database': connection.database, + } + if connection.workgroup: + self.execute_connection_params['WorkgroupName'] = connection.workgroup + if connection.clusterId: + self.execute_connection_params['ClusterIdentifier'] = connection.clusterId + if connection.secretArn: + self.execute_connection_params['SecretArn'] = connection.secretArn + if connection.redshiftUser and connection.clusterId: + # https://boto3.amazonaws.com/v1/documentation/api/1.26.93/reference/services/redshift-data/client/list_databases.html + # We cannot use DbUser with serverless for role federation. + # It must use the current session IAM role, which in this case would be the pivot role. + self.execute_connection_params['DbUser'] = connection.redshiftUser + + def _execute_statement(self, sql: str): + log.info(f'Executing {sql=} with connection {self.execute_connection_params}...') + execute_dict = self.execute_connection_params + execute_dict['Sql'] = sql + execute_statement_response = self.client.execute_statement(**execute_dict) + + execution_finished = False + describe_statement_response = None + while not execution_finished: + describe_statement_response = self.client.describe_statement(Id=execute_statement_response['Id']) + time.sleep(1) + execution_finished = describe_statement_response['Status'] not in ['PICKED', 'STARTED', 'SUBMITTED'] + + if describe_statement_response['Status'] == 'FAILED': + raise Exception(describe_statement_response['Error']) + + log.info(f'Received response {describe_statement_response=}') + return describe_statement_response['Id'] + + @staticmethod + def identifier(name: str) -> str: + return f'"{name}"' + + def fully_qualified_table_name(self, schema: str, table_name: str) -> str: + return f'{RedshiftDataClient.identifier(self.database)}.{RedshiftDataClient.identifier(schema)}.{RedshiftDataClient.identifier(table_name)}' + + def get_redshift_connection_database(self): + databases = [] + try: + log.info(f'Looking for {self.database} in databases...') + + list_databases_response = self.client.list_databases(**self.execute_connection_params) + if 'Databases' in list_databases_response.keys(): + databases = list_databases_response['Databases'] + log.info(f'Returning {databases=}...') + return databases + except ClientError as e: + log.error(e) + raise e + + def list_redshift_schemas(self): + schemas = [] + try: + log.info(f'Fetching {self.database} schemas') + list_schemas_response = self.client.list_schemas(**self.execute_connection_params) + if 'Schemas' in list_schemas_response.keys(): + schemas = list_schemas_response['Schemas'] + + # Remove "internal" schemas + if 'information_schema' in schemas: + schemas.remove('information_schema') + if 'pg_catalog' in schemas: + schemas.remove('pg_catalog') + log.info(f'Returning {schemas=}...') + return schemas + except ClientError as e: + log.error(e) + raise e + + def list_redshift_tables(self, schema: str): + tables_list = [] + try: + log.info(f'Fetching {self.database} tables') + list_tables_response = self.client.list_tables( + **self.execute_connection_params, SchemaPattern=schema, MaxResults=1000 + ) + next_token = list_tables_response.get('NextToken', None) + if 'Tables' in list_tables_response.keys(): + tables_list = list_tables_response['Tables'] + while next_token: + list_tables_response = self.client.list_tables( + **self.execute_connection_params, NextToken=next_token, MaxResults=1000, SchemaPattern=schema + ) + if 'Tables' in list_tables_response.keys(): + tables_list.extend(list_tables_response['Tables']) + next_token = list_tables_response.get('NextToken', None) + + tables = [ + {'name': table['name'], 'type': table['type']} + for table in tables_list + if table['type'] in ['TABLE', 'VIEW'] + ] + log.info(f'Returning {tables=}...') + return tables + except ClientError as e: + log.error(e) + raise e + + def list_redshift_table_columns(self, schema: str, table: str): + columns_list = [] + try: + log.info(f'Fetching {self.database} tables') + response = self.client.describe_table( + **self.execute_connection_params, Schema=schema, Table=table, MaxResults=1000 + ) + next_token = response.get('NextToken', None) + if 'ColumnList' in response.keys(): + columns_list = response['ColumnList'] + while next_token: + response = self.client.describe_table( + **self.execute_connection_params, + Schema=schema, + Table=table, + MaxResults=1000, + NextToken=next_token, + ) + if 'ColumnList' in response.keys(): + columns_list.extend(response['ColumnList']) + next_token = response.get('NextToken', None) + for col in columns_list: + col['nullable'] = True if col['nullable'] == 1 else False + log.info(f'Returning {columns_list=}') + return columns_list + except ClientError as e: + log.error(e) + raise e diff --git a/backend/dataall/modules/redshift_datasets/aws/redshift_serverless.py b/backend/dataall/modules/redshift_datasets/aws/redshift_serverless.py new file mode 100644 index 000000000..5294f626a --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/aws/redshift_serverless.py @@ -0,0 +1,34 @@ +import logging +from typing import List +from botocore.exceptions import ClientError + +from dataall.base.aws.sts import SessionHelper + + +log = logging.getLogger(__name__) + + +class RedshiftServerlessClient: + def __init__(self, account_id: str, region: str, role=None) -> None: + session = SessionHelper.remote_session(accountid=account_id, region=region, role=role) + self.client = session.client(service_name='redshift-serverless', region_name=region) + + def get_namespace_by_id(self, namespace_id: str): + response = self.client.list_namespaces() + namespaces = response.get('namespaces', []) + namespaces_filtered = [namespace for namespace in namespaces if namespace['namespaceId'] == namespace_id] + return namespaces_filtered[0] if namespaces_filtered else None + + def list_workgroups_in_namespace(self, namespace_name: str) -> List[dict]: + response = self.client.list_workgroups() + workgroups = response.get('workgroups', []) + return [wg for wg in workgroups if wg['namespaceName'] == namespace_name] + + def get_workgroup_arn(self, workgroup_name: str) -> str: + try: + log.info(f'Getting arn of {workgroup_name=}') + response = self.client.get_workgroup(workgroupName=workgroup_name) + return response.get('workgroup').get('workgroupArn') + except ClientError as e: + log.error(e) + raise e diff --git a/backend/dataall/modules/redshift_datasets/cdk/__init__.py b/backend/dataall/modules/redshift_datasets/cdk/__init__.py new file mode 100644 index 000000000..3b590a010 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/cdk/__init__.py @@ -0,0 +1,3 @@ +from dataall.modules.redshift_datasets.cdk import pivot_role_redshift_policy + +__all__ = ['pivot_role_redshift_policy'] diff --git a/backend/dataall/modules/redshift_datasets/cdk/pivot_role_redshift_policy.py b/backend/dataall/modules/redshift_datasets/cdk/pivot_role_redshift_policy.py new file mode 100644 index 000000000..8f7875b25 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/cdk/pivot_role_redshift_policy.py @@ -0,0 +1,100 @@ +import os +from aws_cdk import aws_iam as iam + +from dataall.base import db +from dataall.base.aws.sts import SessionHelper +from dataall.base.utils.iam_policy_utils import split_policy_with_resources_in_statements +from dataall.core.environment.cdk.pivot_role_stack import PivotRoleStatementSet +from dataall.modules.redshift_datasets.db.redshift_connection_repositories import RedshiftConnectionRepository +from dataall.modules.redshift_datasets.aws.redshift_serverless import RedshiftServerlessClient + + +class RedshiftDatasetsPivotRole(PivotRoleStatementSet): + """ + Class including all permissions needed by the pivot role to work with Amazon Redshift. + """ + + def get_statements(self): + base_statements = [ + iam.PolicyStatement( + sid='RedshiftSecretsManager', + effect=iam.Effect.ALLOW, + actions=[ + 'secretsmanager:GetSecretValue', + ], + resources=[ + f'arn:aws:secretsmanager:{self.region}:{self.account}:secret:*', + ], + conditions={ + 'StringEquals': { + 'aws:ResourceTag/dataall': 'True', + }, + }, + ), + iam.PolicyStatement( + sid='RedshiftReadAllResources', # These permissions can only be applied to * + effect=iam.Effect.ALLOW, + actions=[ + 'redshift-data:DescribeStatement', + 'redshift:DescribeClusters', + 'redshift-serverless:ListNamespaces', + 'redshift-serverless:ListWorkgroups', + ], + resources=[ + '*', + ], + ), + iam.PolicyStatement( + sid='RedshiftRead', + effect=iam.Effect.ALLOW, + actions=[ + 'redshift-data:ListDatabases', + 'redshift-serverless:GetWorkgroup', + 'redshift:GetClusterCredentials', + ], + resources=[ + f'arn:aws:redshift-serverless:{self.region}:{self.account}:workgroup/*', + f'arn:aws:redshift:{self.region}:{self.account}:cluster:*', + f'arn:aws:redshift:{self.region}:{self.account}:dbuser:*/*', + f'arn:aws:redshift:{self.region}:{self.account}:dbname:*/*', + ], + ), + ] + engine = db.get_engine(envname=os.environ.get('envname', 'local')) + with engine.scoped_session() as session: + connections = RedshiftConnectionRepository.list_environment_redshift_connections( + session, environment_uri=self.environmentUri + ) + additional_statements = [] + if connections: + cdk_look_up_role_arn = SessionHelper.get_cdk_look_up_role_arn( + accountid=self.account, region=self.region + ) + rs_client = RedshiftServerlessClient( + account_id=self.account, region=self.region, role=cdk_look_up_role_arn + ) + cluster_arns = [ + f'arn:aws:redshift:{self.region}:{self.account}:cluster:{conn.clusterId}' + for conn in connections + if conn.clusterId != '' + ] + workgroup_arns = [ + rs_client.get_workgroup_arn(workgroup_name=conn.workgroup) + for conn in connections + if conn.workgroup != '' + ] + additional_statements.extend( + split_policy_with_resources_in_statements( + base_sid='RedshiftData', + effect=iam.Effect.ALLOW, + actions=[ + 'redshift-data:ListSchemas', + 'redshift-data:ListTables', + 'redshift-data:ExecuteStatement', + 'redshift-data:DescribeTable', + ], + resources=cluster_arns + workgroup_arns, + ) + ) + + return base_statements + additional_statements diff --git a/backend/dataall/modules/redshift_datasets/db/__init__.py b/backend/dataall/modules/redshift_datasets/db/__init__.py new file mode 100644 index 000000000..104b49a42 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/db/__init__.py @@ -0,0 +1 @@ +"""Database logic for datasets""" diff --git a/backend/dataall/modules/redshift_datasets/db/redshift_connection_repositories.py b/backend/dataall/modules/redshift_datasets/db/redshift_connection_repositories.py new file mode 100644 index 000000000..1e2a779ab --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/db/redshift_connection_repositories.py @@ -0,0 +1,77 @@ +import logging + +from sqlalchemy import or_ +from sqlalchemy.orm import Query +from dataall.base.db import exceptions +from dataall.core.environment.services.environment_resource_manager import EnvironmentResource +from dataall.base.db import paginate +from dataall.modules.redshift_datasets.db.redshift_models import RedshiftConnection + +logger = logging.getLogger(__name__) + + +class RedshiftConnectionEnvironmentResource(EnvironmentResource): + """Actions performed on any environment resource on environment operations""" + + @staticmethod + def delete_env(session, environment): + RedshiftConnectionRepository.delete_all_environment_connections(session, environment.environmentUri) + + +class RedshiftConnectionRepository: + """DAO layer for Redshift Connections""" + + _DEFAULT_PAGE = 1 + _DEFAULT_PAGE_SIZE = 10 + + @staticmethod + def save_redshift_connection(session, connection): + """Save Redshift Connection to the database""" + session.add(connection) + session.commit() + + @staticmethod + def get_redshift_connection(session, uri) -> RedshiftConnection: + """Find Redshift Connection by URI""" + connection = session.query(RedshiftConnection).get(uri) + if not connection: + raise exceptions.ObjectNotFound('RedshiftConnection', uri) + return connection + + @staticmethod + def _query_user_redshift_connections(session, username, groups, filter) -> Query: + query = session.query(RedshiftConnection).filter( + or_( + RedshiftConnection.owner == username, + RedshiftConnection.SamlGroupName.in_(groups), + ) + ) + if filter and filter.get('environmentUri'): + query = query.filter(RedshiftConnection.environmentUri == filter.get('environmentUri')) + if filter and filter.get('groupUri'): + query = query.filter(RedshiftConnection.SamlGroupName == filter.get('groupUri')) + if filter and filter.get('term'): + query = query.filter( + or_( + RedshiftConnection.description.ilike(filter.get('term') + '%%'), + RedshiftConnection.label.ilike(filter.get('term') + '%%'), + ) + ) + return query.order_by(RedshiftConnection.label) + + @staticmethod + def list_environment_redshift_connections(session, environment_uri): + query = session.query(RedshiftConnection).filter(RedshiftConnection.environmentUri == environment_uri) + return query.order_by(RedshiftConnection.label).all() + + @staticmethod + def paginated_user_redshift_connections(session, username, groups, filter={}) -> dict: + return paginate( + query=RedshiftConnectionRepository._query_user_redshift_connections(session, username, groups, filter), + page=filter.get('page', RedshiftConnectionRepository._DEFAULT_PAGE), + page_size=filter.get('pageSize', RedshiftConnectionRepository._DEFAULT_PAGE_SIZE), + ).to_dict() + + @staticmethod + def delete_all_environment_connections(session, environment_uri): + session.query(RedshiftConnection).filter(RedshiftConnection.environmentUri == environment_uri).delete() diff --git a/backend/dataall/modules/redshift_datasets/db/redshift_dataset_repositories.py b/backend/dataall/modules/redshift_datasets/db/redshift_dataset_repositories.py new file mode 100644 index 000000000..3b9438774 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/db/redshift_dataset_repositories.py @@ -0,0 +1,141 @@ +import logging + +from sqlalchemy import or_, and_ +from dataall.core.activity.db.activity_models import Activity +from dataall.core.environment.db.environment_models import Environment +from dataall.core.organizations.db.organization_repositories import OrganizationRepository +from dataall.base.db import paginate +from dataall.base.db.exceptions import ObjectNotFound +from dataall.modules.datasets_base.services.datasets_enums import ConfidentialityClassification, Language +from dataall.core.environment.services.environment_resource_manager import EnvironmentResource +from dataall.modules.redshift_datasets.db.redshift_models import RedshiftDataset, RedshiftTable + +logger = logging.getLogger(__name__) + + +class RedshiftDatasetEnvironmentResource(EnvironmentResource): + """Actions performed on any environment resource on environment operations""" + + @staticmethod + def count_resources(session, environment, group_uri) -> int: + return RedshiftDatasetRepository.count_environment_group_datasets(session, environment, group_uri) + + +class RedshiftDatasetRepository: + """DAO layer for Redshift Datasets""" + + _DEFAULT_PAGE = 1 + _DEFAULT_PAGE_SIZE = 10 + + @classmethod + def create_redshift_dataset(cls, session, username, env: Environment, data: dict) -> RedshiftDataset: + organization = OrganizationRepository.get_organization_by_uri(session, env.organizationUri) + dataset = RedshiftDataset( + label=data.get('label'), + owner=username, + description=data.get('description', 'No description provided'), + tags=data.get('tags', []), + AwsAccountId=env.AwsAccountId, + SamlAdminGroupName=data['SamlAdminGroupName'], + region=env.region, + environmentUri=env.environmentUri, + organizationUri=env.organizationUri, + language=data.get('language', Language.English.value), + confidentiality=data.get('confidentiality', ConfidentialityClassification.Unclassified.value), + topics=data.get('topics', []), + businessOwnerEmail=data.get('businessOwnerEmail', ''), + businessOwnerDelegationEmails=data.get('businessOwnerDelegationEmails', []), + stewards=data.get('stewards') if data.get('stewards') else data['SamlAdminGroupName'], + autoApprovalEnabled=data.get('autoApprovalEnabled', False), + connectionUri=data.get('connectionUri'), + schema=data.get('schema'), + ) + session.add(dataset) + session.commit() + + activity = Activity( + action='redshift-dataset:import', + label='redshift-dataset:import', + owner=dataset.owner, + summary=f'{dataset.owner} imported redshift dataset {dataset.name} in {env.name} on organization {organization.name}', + targetUri=dataset.datasetUri, + targetType='redshift-dataset', + ) + session.add(activity) + session.commit() + return dataset + + @staticmethod + def get_redshift_dataset_by_uri(session, dataset_uri) -> RedshiftDataset: + dataset: RedshiftDataset = session.query(RedshiftDataset).get(dataset_uri) + if not dataset: + raise ObjectNotFound('RedshiftDataset', dataset_uri) + return dataset + + @staticmethod + def create_redshift_table(session, username, dataset_uri, data: dict) -> RedshiftTable: + dataset = RedshiftDatasetRepository.get_redshift_dataset_by_uri(session, dataset_uri) + table = RedshiftTable( + datasetUri=dataset.datasetUri, + owner=username, + name=data.get('name'), + label=data.get('name'), + description=data.get('description', 'No description provided'), + tags=data.get('tags', []), + topics=data.get('topics', []), + ) + session.add(table) + session.commit() + return table + + @staticmethod + def get_redshift_table_by_uri(session, table_uri) -> RedshiftTable: + table: RedshiftTable = session.query(RedshiftTable).get(table_uri) + if not table: + raise ObjectNotFound('RedshiftTable', table_uri) + return table + + @staticmethod + def _query_redshift_dataset_tables(session, dataset_uri, filter: dict = None): + query = session.query(RedshiftTable).filter(RedshiftTable.datasetUri == dataset_uri) + if filter and filter.get('term'): + query = query.filter( + or_( + *[ + RedshiftTable.name.ilike('%' + filter.get('term') + '%'), + RedshiftTable.label.ilike('%' + filter.get('term') + '%'), + ] + ) + ) + return query + + @staticmethod + def list_redshift_dataset_tables(session, dataset_uri, filter: dict = None): + query = RedshiftDatasetRepository._query_redshift_dataset_tables(session, dataset_uri, filter) + return query.order_by(RedshiftTable.label).all() + + @staticmethod + def paginated_redshift_dataset_tables(session, dataset_uri, data=None) -> dict: + query = RedshiftDatasetRepository._query_redshift_dataset_tables(session, dataset_uri, data) + return paginate( + query=query, + page_size=data.get('pageSize', RedshiftDatasetRepository._DEFAULT_PAGE_SIZE), + page=data.get('page', RedshiftDatasetRepository._DEFAULT_PAGE), + ).to_dict() + + @staticmethod + def count_dataset_tables(session, dataset_uri) -> int: + return RedshiftDatasetRepository._query_redshift_dataset_tables(session, dataset_uri).count() + + @staticmethod + def count_environment_group_datasets(session, environment, group_uri) -> int: + return ( + session.query(RedshiftDataset) + .filter( + and_( + RedshiftDataset.environmentUri == environment.environmentUri, + RedshiftDataset.SamlAdminGroupName == group_uri, + ) + ) + .count() + ) diff --git a/backend/dataall/modules/redshift_datasets/db/redshift_models.py b/backend/dataall/modules/redshift_datasets/db/redshift_models.py new file mode 100644 index 000000000..e2012b922 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/db/redshift_models.py @@ -0,0 +1,41 @@ +from sqlalchemy import Column, String, ForeignKey +from sqlalchemy.dialects.postgresql import ARRAY +from dataall.modules.datasets_base.db.dataset_models import DatasetBase +from dataall.modules.datasets_base.services.datasets_enums import DatasetTypes +from dataall.base.db import Resource, Base, utils + + +class RedshiftConnection(Base, Resource): + __tablename__ = 'redshift_connection' + connectionUri = Column(String, primary_key=True, default=utils.uuid('connection')) + environmentUri = Column(String, ForeignKey('environment.environmentUri'), nullable=False) + SamlGroupName = Column(String, nullable=False) + redshiftType = Column(String, nullable=False) + clusterId = Column(String, nullable=True) + nameSpaceId = Column(String, nullable=True) + workgroup = Column(String, nullable=True) + database = Column(String, nullable=False) + redshiftUser = Column(String, nullable=True) + secretArn = Column(String, nullable=True) + + +class RedshiftDataset(DatasetBase): + __tablename__ = 'redshift_dataset' + datasetUri = Column(String, ForeignKey('dataset.datasetUri'), primary_key=True) + connectionUri = Column(String, ForeignKey('redshift_connection.connectionUri'), nullable=False) + schema = Column(String, nullable=False) + + __mapper_args__ = { + 'polymorphic_identity': DatasetTypes.Redshift, + } + + +class RedshiftTable(Base, Resource): + __tablename__ = 'redshift_table' + datasetUri = Column(String, ForeignKey('redshift_dataset.datasetUri', ondelete='CASCADE'), nullable=False) + rsTableUri = Column(String, primary_key=True, default=utils.uuid('rs_table')) + topics = Column(ARRAY(String), nullable=True) + + @classmethod + def uri(cls): + return cls.rsTableUri diff --git a/backend/dataall/modules/redshift_datasets/indexers/__init__.py b/backend/dataall/modules/redshift_datasets/indexers/__init__.py new file mode 100644 index 000000000..faf66363b --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/indexers/__init__.py @@ -0,0 +1 @@ +"""Contains dataset related indexers for OpenSearch""" diff --git a/backend/dataall/modules/redshift_datasets/indexers/dataset_indexer.py b/backend/dataall/modules/redshift_datasets/indexers/dataset_indexer.py new file mode 100644 index 000000000..73cfe57f1 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/indexers/dataset_indexer.py @@ -0,0 +1,62 @@ +"""Indexes Datasets in OpenSearch""" + +import re + +from dataall.core.environment.services.environment_service import EnvironmentService +from dataall.core.organizations.db.organization_repositories import OrganizationRepository +from dataall.modules.vote.db.vote_repositories import VoteRepository +from dataall.modules.redshift_datasets.db.redshift_dataset_repositories import RedshiftDatasetRepository +from dataall.modules.redshift_datasets.db.redshift_connection_repositories import RedshiftConnectionRepository +from dataall.modules.redshift_datasets.services.redshift_enums import RedshiftType +from dataall.modules.redshift_datasets.services.redshift_constants import ( + VOTE_REDSHIFT_DATASET_NAME, + INDEXER_REDSHIFT_DATASET_NAME, +) +from dataall.modules.catalog.indexers.base_indexer import BaseIndexer + + +class DatasetIndexer(BaseIndexer): + @classmethod + def upsert(cls, session, dataset_uri: str): + dataset = RedshiftDatasetRepository.get_redshift_dataset_by_uri(session=session, dataset_uri=dataset_uri) + connection = RedshiftConnectionRepository.get_redshift_connection(session=session, uri=dataset.connectionUri) + + if dataset: + env = EnvironmentService.get_environment_by_uri(session, dataset.environmentUri) + org = OrganizationRepository.get_organization_by_uri(session, dataset.organizationUri) + + count_tables = RedshiftDatasetRepository.count_dataset_tables(session=session, dataset_uri=dataset_uri) + count_upvotes = VoteRepository.count_upvotes(session, dataset_uri, target_type=VOTE_REDSHIFT_DATASET_NAME) + + glossary = BaseIndexer._get_target_glossary_terms(session, dataset_uri) + BaseIndexer._index( + doc_id=dataset_uri, + doc={ + 'name': dataset.name, + 'owner': dataset.owner, + 'label': dataset.label, + 'admins': dataset.SamlAdminGroupName, + 'database': connection.database, + 'schema': dataset.schema, + 'source': connection.clusterId + if connection.redshiftType == RedshiftType.Cluster.value + else connection.nameSpaceId, + 'resourceKind': INDEXER_REDSHIFT_DATASET_NAME, + 'description': dataset.description, + 'classification': re.sub('[^A-Za-z0-9]+', '', dataset.confidentiality), + 'tags': [t.replace('-', '') for t in dataset.tags or []], + 'topics': dataset.topics, + 'region': dataset.region.replace('-', ''), + 'environmentUri': env.environmentUri, + 'environmentName': env.name, + 'organizationUri': org.organizationUri, + 'organizationName': org.name, + 'created': dataset.created, + 'updated': dataset.updated, + 'deleted': dataset.deleted, + 'glossary': glossary, + 'tables': count_tables, + 'upvotes': count_upvotes, + }, + ) + return dataset diff --git a/backend/dataall/modules/redshift_datasets/indexers/table_indexer.py b/backend/dataall/modules/redshift_datasets/indexers/table_indexer.py new file mode 100644 index 000000000..0b5d3cfd2 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/indexers/table_indexer.py @@ -0,0 +1,59 @@ +"""Indexes DatasetTable in OpenSearch""" + +import re + +from dataall.core.environment.services.environment_service import EnvironmentService +from dataall.core.organizations.db.organization_repositories import OrganizationRepository +from dataall.modules.redshift_datasets.db.redshift_dataset_repositories import RedshiftDatasetRepository +from dataall.modules.redshift_datasets.db.redshift_connection_repositories import RedshiftConnectionRepository +from dataall.modules.redshift_datasets.services.redshift_enums import RedshiftType +from dataall.modules.catalog.indexers.base_indexer import BaseIndexer + + +class DatasetTableIndexer(BaseIndexer): + @classmethod + def upsert(cls, session, table_uri: str, dataset=None, env=None, org=None): + table = RedshiftDatasetRepository.get_redshift_table_by_uri(session, table_uri) + + if table: + dataset = ( + RedshiftDatasetRepository.get_redshift_dataset_by_uri(session, table.datasetUri) + if not dataset + else dataset + ) + connection = RedshiftConnectionRepository.get_redshift_connection(session, dataset.connectionUri) + env = EnvironmentService.get_environment_by_uri(session, dataset.environmentUri) if not env else env + org = OrganizationRepository.get_organization_by_uri(session, dataset.organizationUri) if not org else org + glossary = BaseIndexer._get_target_glossary_terms(session, table_uri) + + tags = table.tags if table.tags else [] + BaseIndexer._index( + doc_id=table_uri, + doc={ + 'name': table.name, + 'admins': dataset.SamlAdminGroupName, + 'owner': table.owner, + 'label': table.label, + 'resourceKind': 'redshifttable', + 'description': table.description, + 'database': connection.database, + 'schema': dataset.schema, + 'source': connection.clusterId + if connection.redshiftType == RedshiftType.Cluster.value + else connection.nameSpaceId, + 'classification': re.sub('[^A-Za-z0-9]+', '', dataset.confidentiality), + 'tags': [t.replace('-', '') for t in tags or []], + 'topics': dataset.topics, + 'region': dataset.region.replace('-', ''), + 'datasetUri': table.datasetUri, + 'environmentUri': env.environmentUri, + 'environmentName': env.name, + 'organizationUri': org.organizationUri, + 'organizationName': org.name, + 'created': table.created, + 'updated': table.updated, + 'deleted': table.deleted, + 'glossary': glossary, + }, + ) + return table diff --git a/backend/dataall/modules/redshift_datasets/services/__init__.py b/backend/dataall/modules/redshift_datasets/services/__init__.py new file mode 100644 index 000000000..03ef29863 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/services/__init__.py @@ -0,0 +1 @@ +"""Contains business logic for datasets""" diff --git a/backend/dataall/modules/redshift_datasets/services/redshift_connection_permissions.py b/backend/dataall/modules/redshift_datasets/services/redshift_connection_permissions.py new file mode 100644 index 000000000..bad6ab72a --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/services/redshift_connection_permissions.py @@ -0,0 +1,59 @@ +from itertools import chain +from dataall.core.permissions.services.tenant_permissions import TENANT_ALL, TENANT_ALL_WITH_DESC +from dataall.core.permissions.services.environment_permissions import ( + ENVIRONMENT_INVITED, + ENVIRONMENT_INVITATION_REQUEST, + ENVIRONMENT_INVITED_DEFAULT, + ENVIRONMENT_ALL, +) +from dataall.core.permissions.services.resources_permissions import ( + RESOURCES_ALL, + RESOURCES_ALL_WITH_DESC, +) + +""" +REDSHIFT CONNECTION TENANT PERMISSIONS +""" + +MANAGE_REDSHIFT_CONNECTIONS = 'MANAGE_REDSHIFT_CONNECTIONS' + +TENANT_ALL.append(MANAGE_REDSHIFT_CONNECTIONS) +TENANT_ALL_WITH_DESC[MANAGE_REDSHIFT_CONNECTIONS] = 'Manage Redshift connections' + +""" +REDSHIFT CONNECTION PERMISSIONS +""" +GET_REDSHIFT_CONNECTION = 'GET_REDSHIFT_CONNECTION' +REDSHIFT_CONNECTION_READ = [GET_REDSHIFT_CONNECTION] + +DELETE_REDSHIFT_CONNECTION = 'DELETE_REDSHIFT_CONNECTION' +REDSHIFT_CONNECTION_WRITE = [DELETE_REDSHIFT_CONNECTION] + +REDSHIFT_CONNECTION_ALL = list(set(REDSHIFT_CONNECTION_WRITE + REDSHIFT_CONNECTION_READ)) +RESOURCES_ALL.extend(REDSHIFT_CONNECTION_ALL) + +for perm in chain(REDSHIFT_CONNECTION_ALL): + RESOURCES_ALL_WITH_DESC[perm] = perm + + +""" +REDSHIFT CONNECTION PERMISSIONS FOR ENVIRONMENT +""" + +LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS = 'LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS' +CREATE_REDSHIFT_CONNECTION = 'CREATE_REDSHIFT_CONNECTION' + +ENVIRONMENT_INVITED.append(LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS) +ENVIRONMENT_INVITED.append(CREATE_REDSHIFT_CONNECTION) + +ENVIRONMENT_INVITATION_REQUEST.append(CREATE_REDSHIFT_CONNECTION) # Selectable in invitation toogle +ENVIRONMENT_INVITED_DEFAULT.append(LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS) # Granted by default + + +ENVIRONMENT_ALL.append(LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS) +ENVIRONMENT_ALL.append(CREATE_REDSHIFT_CONNECTION) + +RESOURCES_ALL.append(LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS) +RESOURCES_ALL.append(CREATE_REDSHIFT_CONNECTION) +RESOURCES_ALL_WITH_DESC[LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS] = 'LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS' +RESOURCES_ALL_WITH_DESC[CREATE_REDSHIFT_CONNECTION] = 'Create Redshift Connection in this environment' diff --git a/backend/dataall/modules/redshift_datasets/services/redshift_connection_service.py b/backend/dataall/modules/redshift_datasets/services/redshift_connection_service.py new file mode 100644 index 000000000..f658b17b3 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/services/redshift_connection_service.py @@ -0,0 +1,163 @@ +import logging +from dataall.base.context import get_context +from dataall.base.db import exceptions +from dataall.core.permissions.services.resource_policy_service import ResourcePolicyService +from dataall.core.permissions.services.tenant_policy_service import TenantPolicyService +from dataall.core.permissions.services.group_policy_service import GroupPolicyService +from dataall.core.environment.services.environment_service import EnvironmentService +from dataall.core.stacks.services.stack_service import StackService +from dataall.modules.redshift_datasets.db.redshift_connection_repositories import RedshiftConnectionRepository + +from dataall.modules.redshift_datasets.services.redshift_connection_permissions import ( + MANAGE_REDSHIFT_CONNECTIONS, + REDSHIFT_CONNECTION_ALL, + DELETE_REDSHIFT_CONNECTION, + GET_REDSHIFT_CONNECTION, + CREATE_REDSHIFT_CONNECTION, + LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS, +) +from dataall.modules.redshift_datasets.db.redshift_models import RedshiftConnection +from dataall.modules.redshift_datasets.aws.redshift_data import RedshiftDataClient +from dataall.modules.redshift_datasets.aws.redshift_serverless import RedshiftServerlessClient +from dataall.modules.redshift_datasets.aws.redshift import RedshiftClient + +log = logging.getLogger(__name__) + + +class RedshiftConnectionService: + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_CONNECTIONS) + @ResourcePolicyService.has_resource_permission(CREATE_REDSHIFT_CONNECTION) + @GroupPolicyService.has_group_permission(CREATE_REDSHIFT_CONNECTION) + def create_redshift_connection(uri, admin_group, data: dict) -> RedshiftConnection: + context = get_context() + with context.db_engine.scoped_session() as session: + environment = EnvironmentService.get_environment_by_uri(session, uri) + connection = RedshiftConnection( + label=data.get('connectionName'), + name=data.get('connectionName'), + owner=context.username, + environmentUri=environment.environmentUri, + SamlGroupName=admin_group, + redshiftType=data.get('redshiftType'), + clusterId=data.get('clusterId', ''), + nameSpaceId=data.get('nameSpaceId', ''), + workgroup=data.get('workgroup', ''), + database=data.get('database'), + redshiftUser=data.get('redshiftUser', ''), + secretArn=data.get('secretArn', ''), + ) + RedshiftConnectionService._check_redshift_connection( + account_id=environment.AwsAccountId, region=environment.region, connection=connection + ) + RedshiftConnectionRepository.save_redshift_connection(session, connection) + + ResourcePolicyService.attach_resource_policy( + session=session, + group=connection.SamlGroupName, + permissions=REDSHIFT_CONNECTION_ALL, + resource_uri=connection.connectionUri, + resource_type=RedshiftConnection.__name__, + ) + StackService.deploy_stack(targetUri=environment.environmentUri) + return connection + + @staticmethod + @ResourcePolicyService.has_resource_permission(GET_REDSHIFT_CONNECTION) + def get_redshift_connection_by_uri(uri) -> RedshiftConnection: + with get_context().db_engine.scoped_session() as session: + connection = RedshiftConnectionRepository.get_redshift_connection(session, uri) + if not connection: + raise exceptions.ObjectNotFound('RedshiftConnection', uri) + return connection + + @staticmethod + def _get_redshift_connection(session, uri) -> RedshiftConnection: + return RedshiftConnectionRepository.get_redshift_connection(session, uri) + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_CONNECTIONS) + @ResourcePolicyService.has_resource_permission(DELETE_REDSHIFT_CONNECTION) + def delete_redshift_connection(uri) -> bool: + context = get_context() + with context.db_engine.scoped_session() as session: + connection = RedshiftConnectionService._get_redshift_connection(session=session, uri=uri) + ResourcePolicyService.delete_resource_policy( + session=session, + resource_uri=connection.connectionUri, + group=connection.SamlGroupName, + ) + session.delete(connection) + session.commit() + return True + + @staticmethod + @ResourcePolicyService.has_resource_permission(LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS) + def list_environment_redshift_connections(uri, filter): + context = get_context() + with context.db_engine.scoped_session() as session: + connections = RedshiftConnectionRepository.paginated_user_redshift_connections( + session, context.username, context.groups, filter + ) + return connections + + @staticmethod + @ResourcePolicyService.has_resource_permission(GET_REDSHIFT_CONNECTION) + def list_connection_schemas(uri): + context = get_context() + with context.db_engine.scoped_session() as session: + connection = RedshiftConnectionService.get_redshift_connection_by_uri(uri=uri) + environment = EnvironmentService.get_environment_by_uri(session, connection.environmentUri) + return RedshiftDataClient( + account_id=environment.AwsAccountId, region=environment.region, connection=connection + ).list_redshift_schemas() + + @staticmethod + @ResourcePolicyService.has_resource_permission(GET_REDSHIFT_CONNECTION) + def list_schema_tables(uri, schema): + context = get_context() + with context.db_engine.scoped_session() as session: + connection = RedshiftConnectionService.get_redshift_connection_by_uri(uri=uri) + environment = EnvironmentService.get_environment_by_uri(session, connection.environmentUri) + response = RedshiftDataClient( + account_id=environment.AwsAccountId, region=environment.region, connection=connection + ).list_redshift_tables(schema) + return response + + @staticmethod + def _check_redshift_connection(account_id: str, region: str, connection: RedshiftConnection): + if connection.nameSpaceId: + if ( + namespace := RedshiftServerlessClient(account_id=account_id, region=region).get_namespace_by_id( + connection.nameSpaceId + ) + ) is None: + raise Exception( + f'Redshift namespaceId {connection.nameSpaceId} does not exist. Remember to introduce the Id and not the name of the namespace.' + ) + if connection.workgroup and connection.workgroup not in [ + workgroup['workgroupName'] + for workgroup in RedshiftServerlessClient( + account_id=account_id, region=region + ).list_workgroups_in_namespace(namespace['namespaceName']) + ]: + raise Exception( + f'Redshift workgroup {connection.workgroup} does not exist or is not associated to namespace {connection.nameSpaceId}' + ) + + if connection.clusterId and not RedshiftClient(account_id=account_id, region=region).describe_cluster( + connection.clusterId + ): + raise Exception( + f'Redshift cluster {connection.clusterId} does not exist or cannot be accessed with these parameters' + ) + + try: + RedshiftDataClient( + account_id=account_id, region=region, connection=connection + ).get_redshift_connection_database() + except Exception as e: + raise Exception( + f'Redshift database {connection.database} does not exist or cannot be accessed with these parameters: {e}' + ) + return diff --git a/backend/dataall/modules/redshift_datasets/services/redshift_constants.py b/backend/dataall/modules/redshift_datasets/services/redshift_constants.py new file mode 100644 index 000000000..c37fdee92 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/services/redshift_constants.py @@ -0,0 +1,10 @@ +GLOSSARY_REDSHIFT_DATASET_NAME = 'RedshiftDataset' +GLOSSARY_REDSHIFT_DATASET_TABLE_NAME = 'RedshiftDatasetTable' + +FEED_REDSHIFT_DATASET_NAME = 'RedshiftDataset' +FEED_REDSHIFT_DATASET_TABLE_NAME = 'RedshiftDatasetTable' + +VOTE_REDSHIFT_DATASET_NAME = 'redshiftdataset' + +INDEXER_REDSHIFT_DATASET_NAME = 'redshiftdataset' +INDEXER_REDSHIFT_DATASET_TABLE_NAME = 'redshiftdatasettable' diff --git a/backend/dataall/modules/redshift_datasets/services/redshift_dataset_permissions.py b/backend/dataall/modules/redshift_datasets/services/redshift_dataset_permissions.py new file mode 100644 index 000000000..ee3c04c02 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/services/redshift_dataset_permissions.py @@ -0,0 +1,71 @@ +from itertools import chain + +from dataall.core.permissions.services.environment_permissions import ( + ENVIRONMENT_INVITED, + ENVIRONMENT_INVITATION_REQUEST, + ENVIRONMENT_ALL, +) +from dataall.core.permissions.services.tenant_permissions import TENANT_ALL, TENANT_ALL_WITH_DESC +from dataall.core.permissions.services.resources_permissions import ( + RESOURCES_ALL, + RESOURCES_ALL_WITH_DESC, +) + +""" +REDSHIFT DATASET TENANT PERMISSIONS +""" + +MANAGE_REDSHIFT_DATASETS = 'MANAGE_REDSHIFT_DATASETS' + +TENANT_ALL.append(MANAGE_REDSHIFT_DATASETS) +TENANT_ALL_WITH_DESC[MANAGE_REDSHIFT_DATASETS] = 'Manage Redshift datasets' + +""" +REDSHIFT DATASET RESOURCE-GROUP PERMISSIONS +""" + +GET_REDSHIFT_DATASET = 'GET_REDSHIFT_DATASET' + +REDSHIFT_DATASET_READ = [GET_REDSHIFT_DATASET] + +ADD_TABLES_REDSHIFT_DATASET = 'ADD_TABLES_REDSHIFT_DATASET' +DELETE_REDSHIFT_DATASET = 'DELETE_REDSHIFT_DATASET' +UPDATE_REDSHIFT_DATASET = 'UPDATE_REDSHIFT_DATASET' + +REDSHIFT_DATASET_WRITE = [ADD_TABLES_REDSHIFT_DATASET, DELETE_REDSHIFT_DATASET, UPDATE_REDSHIFT_DATASET] + +REDSHIFT_DATASET_ALL = list(set(REDSHIFT_DATASET_WRITE + REDSHIFT_DATASET_READ)) +RESOURCES_ALL.extend(REDSHIFT_DATASET_ALL) + +""" +REDSHIFT DATASET TABLE RESOURCE-GROUP PERMISSIONS +""" + +GET_REDSHIFT_DATASET_TABLE = 'GET_REDSHIFT_DATASET_TABLE' +REDSHIFT_DATASET_TABLE_READ = [GET_REDSHIFT_DATASET_TABLE] + +DELETE_REDSHIFT_DATASET_TABLE = 'DELETE_REDSHIFT_DATASET_TABLE' +UPDATE_REDSHIFT_DATASET_TABLE = 'UPDATE_REDSHIFT_DATASET_TABLE' +REDSHIFT_DATASET_TABLE_WRITE = [DELETE_REDSHIFT_DATASET_TABLE, UPDATE_REDSHIFT_DATASET_TABLE] + +REDSHIFT_DATASET_TABLE_ALL = list(set(REDSHIFT_DATASET_TABLE_WRITE + REDSHIFT_DATASET_TABLE_READ)) +RESOURCES_ALL.extend(REDSHIFT_DATASET_TABLE_ALL) + +""" +REDSHIFT DATASET PERMISSIONS FOR ENVIRONMENT +""" + +IMPORT_REDSHIFT_DATASET = 'IMPORT_REDSHIFT_DATASET' + +ENVIRONMENT_INVITED.append(IMPORT_REDSHIFT_DATASET) + +ENVIRONMENT_INVITATION_REQUEST.append(IMPORT_REDSHIFT_DATASET) + +ENVIRONMENT_ALL.append(IMPORT_REDSHIFT_DATASET) + +RESOURCES_ALL.append(IMPORT_REDSHIFT_DATASET) + +for perm in chain(REDSHIFT_DATASET_ALL + REDSHIFT_DATASET_TABLE_ALL): + RESOURCES_ALL_WITH_DESC[perm] = perm + +RESOURCES_ALL_WITH_DESC[IMPORT_REDSHIFT_DATASET] = 'Import Redshift Datasets to this environment' diff --git a/backend/dataall/modules/redshift_datasets/services/redshift_dataset_service.py b/backend/dataall/modules/redshift_datasets/services/redshift_dataset_service.py new file mode 100644 index 000000000..288bc949d --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/services/redshift_dataset_service.py @@ -0,0 +1,371 @@ +import logging + +from dataall.base.context import get_context +from dataall.base.db.paginator import paginate_list +from dataall.core.permissions.services.resource_policy_service import ResourcePolicyService +from dataall.core.permissions.services.tenant_policy_service import TenantPolicyService +from dataall.core.permissions.services.group_policy_service import GroupPolicyService +from dataall.core.environment.services.environment_service import EnvironmentService +from dataall.modules.vote.db.vote_repositories import VoteRepository +from dataall.modules.catalog.db.glossary_repositories import GlossaryRepository + + +from dataall.modules.datasets_base.services.datasets_enums import DatasetRole +from dataall.modules.datasets_base.db.dataset_repositories import DatasetBaseRepository + +from dataall.modules.redshift_datasets.services.redshift_dataset_permissions import ( + MANAGE_REDSHIFT_DATASETS, + IMPORT_REDSHIFT_DATASET, + ADD_TABLES_REDSHIFT_DATASET, + DELETE_REDSHIFT_DATASET, + UPDATE_REDSHIFT_DATASET, + GET_REDSHIFT_DATASET, + REDSHIFT_DATASET_ALL, + REDSHIFT_DATASET_READ, + GET_REDSHIFT_DATASET_TABLE, + DELETE_REDSHIFT_DATASET_TABLE, + UPDATE_REDSHIFT_DATASET_TABLE, + REDSHIFT_DATASET_TABLE_ALL, + REDSHIFT_DATASET_TABLE_READ, +) +from dataall.modules.redshift_datasets.db.redshift_dataset_repositories import RedshiftDatasetRepository +from dataall.modules.redshift_datasets.db.redshift_connection_repositories import RedshiftConnectionRepository +from dataall.modules.redshift_datasets.db.redshift_models import RedshiftDataset, RedshiftTable +from dataall.modules.redshift_datasets.aws.redshift_data import RedshiftDataClient +from dataall.modules.redshift_datasets.indexers.dataset_indexer import DatasetIndexer +from dataall.modules.redshift_datasets.indexers.table_indexer import DatasetTableIndexer +from dataall.modules.redshift_datasets.services.redshift_constants import ( + GLOSSARY_REDSHIFT_DATASET_NAME, + GLOSSARY_REDSHIFT_DATASET_TABLE_NAME, + VOTE_REDSHIFT_DATASET_NAME, +) + + +log = logging.getLogger(__name__) + + +class RedshiftDatasetService: + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_DATASETS) + @ResourcePolicyService.has_resource_permission(IMPORT_REDSHIFT_DATASET) + @GroupPolicyService.has_group_permission(IMPORT_REDSHIFT_DATASET) + def import_redshift_dataset(uri, admin_group, data: dict): + context = get_context() + with context.db_engine.scoped_session() as session: + environment = EnvironmentService.get_environment_by_uri(session, uri) + + dataset = RedshiftDatasetRepository.create_redshift_dataset( + session=session, username=context.username, env=environment, data=data + ) + dataset.userRoleForDataset = DatasetRole.Creator.value + + RedshiftDatasetService._attach_dataset_permissions(session, dataset, environment) + + DatasetIndexer.upsert(session=session, dataset_uri=dataset.datasetUri) + + for table in data.get('tables', []): + rs_table = RedshiftDatasetRepository.create_redshift_table( + session=session, + username=context.username, + dataset_uri=dataset.datasetUri, + data={'name': table}, + ) + RedshiftDatasetService._attach_table_permissions(session, dataset, environment, rs_table) + DatasetTableIndexer.upsert(session=session, table_uri=rs_table.rsTableUri) + + return dataset + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_DATASETS) + @ResourcePolicyService.has_resource_permission(UPDATE_REDSHIFT_DATASET) + def update_redshift_dataset(uri, data: dict): + context = get_context() + username = context.username + with context.db_engine.scoped_session() as session: + dataset: RedshiftDataset = RedshiftDatasetRepository.get_redshift_dataset_by_uri(session, uri) + if data and isinstance(data, dict): + for k in data.keys(): + if k not in ['stewards']: + setattr(dataset, k, data.get(k)) + + if data.get('stewards') and data.get('stewards') != dataset.stewards: + if data.get('stewards') != dataset.SamlAdminGroupName: + RedshiftDatasetService._transfer_stewardship_to_new_stewards(session, dataset, data['stewards']) + dataset.stewards = data['stewards'] + else: + RedshiftDatasetService._transfer_stewardship_to_owners(session, dataset) + dataset.stewards = dataset.SamlAdminGroupName + + ResourcePolicyService.attach_resource_policy( + session=session, + group=dataset.SamlAdminGroupName, + permissions=REDSHIFT_DATASET_ALL, + resource_uri=dataset.datasetUri, + resource_type=RedshiftDataset.__name__, + ) + if data.get('terms'): + GlossaryRepository.set_glossary_terms_links( + session, username, uri, GLOSSARY_REDSHIFT_DATASET_NAME, data.get('terms') + ) + DatasetBaseRepository.update_dataset_activity(session, dataset, username) + + DatasetIndexer.upsert(session, dataset_uri=uri) + return dataset + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_DATASETS) + @ResourcePolicyService.has_resource_permission(DELETE_REDSHIFT_DATASET) + def delete_redshift_dataset(uri): + context = get_context() + with context.db_engine.scoped_session() as session: + dataset: RedshiftDataset = RedshiftDatasetRepository.get_redshift_dataset_by_uri(session, uri) + + # TODO: when adding sharing, add check_on_delete for shared items + tables: [RedshiftTable] = RedshiftDatasetRepository.list_redshift_dataset_tables( + session, dataset.datasetUri + ) + for table in tables: + DatasetTableIndexer.delete_doc(doc_id=table.rsTableUri) + session.delete(table) + + ResourcePolicyService.delete_resource_policy( + session=session, resource_uri=uri, group=dataset.SamlAdminGroupName + ) + env = EnvironmentService.get_environment_by_uri(session, dataset.environmentUri) + if dataset.SamlAdminGroupName != env.SamlGroupName: + ResourcePolicyService.delete_resource_policy(session=session, resource_uri=uri, group=env.SamlGroupName) + if dataset.stewards: + ResourcePolicyService.delete_resource_policy(session=session, resource_uri=uri, group=dataset.stewards) + + DatasetTableIndexer.delete_doc(doc_id=dataset.datasetUri) + RedshiftDatasetService._delete_dataset_term_links(session, uri) + VoteRepository.delete_votes(session, dataset.datasetUri, VOTE_REDSHIFT_DATASET_NAME) + session.delete(dataset) + session.commit() + return True + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_DATASETS) + @ResourcePolicyService.has_resource_permission(ADD_TABLES_REDSHIFT_DATASET) + def add_redshift_dataset_tables(uri, tables): + context = get_context() + datasetUri = uri + with context.db_engine.scoped_session() as session: + dataset = RedshiftDatasetRepository.get_redshift_dataset_by_uri(session, datasetUri) + dataset_tables = RedshiftDatasetRepository.list_redshift_dataset_tables(session, datasetUri) + tables = [new_t for new_t in tables if new_t not in [t.name for t in dataset_tables]] + for table in tables: + rs_table = RedshiftDatasetRepository.create_redshift_table( + session=session, + username=context.username, + dataset_uri=datasetUri, + data={'name': table}, + ) + ResourcePolicyService.attach_resource_policy( + session=session, + group=dataset.SamlAdminGroupName, + permissions=REDSHIFT_DATASET_TABLE_ALL, + resource_uri=rs_table.rsTableUri, + resource_type=RedshiftTable.__name__, + ) + DatasetTableIndexer.upsert(session=session, table_uri=rs_table.rsTableUri) + return True + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_DATASETS) + @ResourcePolicyService.has_resource_permission(DELETE_REDSHIFT_DATASET_TABLE) + def delete_redshift_dataset_table(uri): + context = get_context() + with context.db_engine.scoped_session() as session: + table: RedshiftTable = RedshiftDatasetRepository.get_redshift_table_by_uri(session, uri) + DatasetTableIndexer.delete_doc(doc_id=table.rsTableUri) + session.delete(table) + session.commit() + return True + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_DATASETS) + @ResourcePolicyService.has_resource_permission(UPDATE_REDSHIFT_DATASET_TABLE) + def update_redshift_dataset_table(uri, data: dict): + context = get_context() + username = context.username + with context.db_engine.scoped_session() as session: + table: RedshiftTable = RedshiftDatasetRepository.get_redshift_table_by_uri(session, uri) + if data and isinstance(data, dict): + for k in data.keys(): + setattr(table, k, data.get(k)) + + if data.get('terms'): + GlossaryRepository.set_glossary_terms_links( + session, username, table.rsTableUri, GLOSSARY_REDSHIFT_DATASET_TABLE_NAME, data.get('terms') + ) + DatasetTableIndexer.upsert(session, table_uri=uri) + return table + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_DATASETS) + @ResourcePolicyService.has_resource_permission(GET_REDSHIFT_DATASET) + def get_redshift_dataset(uri): + context = get_context() + with context.db_engine.scoped_session() as session: + dataset = RedshiftDatasetRepository.get_redshift_dataset_by_uri(session, uri) + if dataset.SamlAdminGroupName in context.groups: + dataset.userRoleForDataset = DatasetRole.Admin.value + return dataset + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_DATASETS) + @ResourcePolicyService.has_resource_permission(GET_REDSHIFT_DATASET) + def list_redshift_dataset_tables(uri, filter): + context = get_context() + with context.db_engine.scoped_session() as session: + dataset = RedshiftDatasetRepository.get_redshift_dataset_by_uri(session, uri) + return RedshiftDatasetRepository.paginated_redshift_dataset_tables( + session=session, dataset_uri=dataset.datasetUri, data=filter + ) + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_DATASETS) + @ResourcePolicyService.has_resource_permission(GET_REDSHIFT_DATASET) + def list_redshift_schema_dataset_tables(uri): + with get_context().db_engine.scoped_session() as session: + dataset = RedshiftDatasetRepository.get_redshift_dataset_by_uri(session, uri) + dataset_tables_names = [ + t.name for t in RedshiftDatasetRepository.list_redshift_dataset_tables(session, dataset.datasetUri) + ] + connection = RedshiftConnectionRepository.get_redshift_connection(session, dataset.connectionUri) + environment = EnvironmentService.get_environment_by_uri(session, connection.environmentUri) + tables = RedshiftDataClient( + account_id=environment.AwsAccountId, region=environment.region, connection=connection + ).list_redshift_tables(dataset.schema) + for table in tables: + if table['name'] in dataset_tables_names: + table.update({'alreadyAdded': True}) + else: + table.update({'alreadyAdded': False}) + return tables + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_DATASETS) + @ResourcePolicyService.has_resource_permission(GET_REDSHIFT_DATASET) + def get_dataset_upvotes(uri): + with get_context().db_engine.scoped_session() as session: + return VoteRepository.count_upvotes(session, uri, target_type=VOTE_REDSHIFT_DATASET_NAME) or 0 + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_DATASETS) + @ResourcePolicyService.has_resource_permission(GET_REDSHIFT_DATASET_TABLE) + def get_redshift_dataset_table(uri): + context = get_context() + with context.db_engine.scoped_session() as session: + table = RedshiftDatasetRepository.get_redshift_table_by_uri(session, uri) + return table + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_REDSHIFT_DATASETS) + @ResourcePolicyService.has_resource_permission(GET_REDSHIFT_DATASET_TABLE) + def list_redshift_dataset_table_columns(uri, filter): + context = get_context() + with context.db_engine.scoped_session() as session: + table = RedshiftDatasetRepository.get_redshift_table_by_uri(session=session, table_uri=uri) + dataset = RedshiftDatasetRepository.get_redshift_dataset_by_uri( + session=session, dataset_uri=table.datasetUri + ) + connection = RedshiftConnectionRepository.get_redshift_connection( + session=session, uri=dataset.connectionUri + ) + columns = RedshiftDataClient( + account_id=dataset.AwsAccountId, region=dataset.region, connection=connection + ).list_redshift_table_columns(dataset.schema, table.name) + return paginate_list( + items=columns, page_size=filter.get('pageSize', 10), page=filter.get('page', 1) + ).to_dict() + + @staticmethod + def _delete_dataset_term_links(session, dataset_uri): + tables = [t.rsTableUri for t in RedshiftDatasetRepository.list_redshift_dataset_tables(session, dataset_uri)] + for table_uri in tables: + GlossaryRepository.delete_glossary_terms_links(session, table_uri, GLOSSARY_REDSHIFT_DATASET_TABLE_NAME) + GlossaryRepository.delete_glossary_terms_links(session, dataset_uri, GLOSSARY_REDSHIFT_DATASET_NAME) + + @staticmethod + def _attach_dataset_permissions(session, dataset, environment): + ResourcePolicyService.attach_resource_policy( + session=session, + group=dataset.SamlAdminGroupName, + permissions=REDSHIFT_DATASET_ALL, + resource_uri=dataset.datasetUri, + resource_type=RedshiftDataset.__name__, + ) + if dataset.stewards and dataset.stewards != dataset.SamlAdminGroupName: + ResourcePolicyService.attach_resource_policy( + session=session, + group=dataset.stewards, + permissions=REDSHIFT_DATASET_READ, + resource_uri=dataset.datasetUri, + resource_type=RedshiftDataset.__name__, + ) + + if environment.SamlGroupName != dataset.SamlAdminGroupName: + ResourcePolicyService.attach_resource_policy( + session=session, + group=environment.SamlGroupName, + permissions=REDSHIFT_DATASET_ALL, + resource_uri=dataset.datasetUri, + resource_type=RedshiftDataset.__name__, + ) + + @staticmethod + def _attach_table_permissions(session, dataset, environment, table): + ResourcePolicyService.attach_resource_policy( + session=session, + group=dataset.SamlAdminGroupName, + permissions=REDSHIFT_DATASET_TABLE_ALL, + resource_uri=table.rsTableUri, + resource_type=RedshiftTable.__name__, + ) + if dataset.stewards and dataset.stewards != dataset.SamlAdminGroupName: + ResourcePolicyService.attach_resource_policy( + session=session, + group=dataset.stewards, + permissions=REDSHIFT_DATASET_TABLE_READ, + resource_uri=table.rsTableUri, + resource_type=RedshiftTable.__name__, + ) + if environment.SamlGroupName != dataset.SamlAdminGroupName: + ResourcePolicyService.attach_resource_policy( + session=session, + group=environment.SamlGroupName, + permissions=REDSHIFT_DATASET_TABLE_ALL, + resource_uri=table.rsTableUri, + resource_type=RedshiftTable.__name__, + ) + + @staticmethod + def _transfer_stewardship_to_owners(session, dataset): + env = EnvironmentService.get_environment_by_uri(session, dataset.environmentUri) + if dataset.stewards != env.SamlGroupName: + ResourcePolicyService.delete_resource_policy( + session=session, + group=dataset.stewards, + resource_uri=dataset.datasetUri, + ) + + return dataset + + @staticmethod + def _transfer_stewardship_to_new_stewards(session, dataset, new_stewards): + if dataset.stewards != dataset.SamlAdminGroupName: + ResourcePolicyService.delete_resource_policy( + session=session, + group=dataset.stewards, + resource_uri=dataset.datasetUri, + ) + ResourcePolicyService.attach_resource_policy( + session=session, + group=new_stewards, + permissions=REDSHIFT_DATASET_READ, + resource_uri=dataset.datasetUri, + resource_type=RedshiftDataset.__name__, + ) + return dataset diff --git a/backend/dataall/modules/redshift_datasets/services/redshift_enums.py b/backend/dataall/modules/redshift_datasets/services/redshift_enums.py new file mode 100644 index 000000000..52e052a32 --- /dev/null +++ b/backend/dataall/modules/redshift_datasets/services/redshift_enums.py @@ -0,0 +1,6 @@ +from enum import Enum + + +class RedshiftType(Enum): + Serverless = 'serverless' + Cluster = 'cluster' diff --git a/backend/dataall/modules/s3_datasets_shares/services/s3_share_service.py b/backend/dataall/modules/s3_datasets_shares/services/s3_share_service.py index 255544bc7..f77e55445 100644 --- a/backend/dataall/modules/s3_datasets_shares/services/s3_share_service.py +++ b/backend/dataall/modules/s3_datasets_shares/services/s3_share_service.py @@ -9,6 +9,8 @@ from dataall.core.environment.services.environment_service import EnvironmentService from dataall.core.tasks.db.task_models import Task from dataall.core.tasks.service_handlers import Worker +from dataall.modules.datasets_base.db.dataset_models import DatasetBase +from dataall.modules.datasets_base.db.dataset_repositories import DatasetBaseRepository from dataall.modules.shares_base.db.share_object_repositories import ShareObjectRepository from dataall.modules.shares_base.db.share_state_machines_repositories import ShareStatusRepository from dataall.modules.shares_base.services.share_item_service import ShareItemService @@ -255,8 +257,18 @@ def list_shared_databases_tables_with_env_group(environmentUri: str, groupUri: s @staticmethod def resolve_shared_db_name(GlueDatabaseName: str, shareUri: str, targetEnvAwsAccountId: str, targetEnvRegion: str): - old_shared_db_name = (GlueDatabaseName + '_shared_' + shareUri)[:254] - database = GlueClient( - account_id=targetEnvAwsAccountId, database=old_shared_db_name, region=targetEnvRegion - ).get_glue_database() - return old_shared_db_name if database else GlueDatabaseName + '_shared' + with get_context().db_engine.scoped_session() as session: + share = ShareObjectRepository.get_share_by_uri(session, shareUri) + dataset = DatasetBaseRepository.get_dataset_by_uri(session, share.datasetUri) + try: + datasetGlueDatabase = GlueClient( + account_id=dataset.AwsAccountId, region=dataset.region, database=GlueDatabaseName + ).get_glue_database_from_catalog() + except Exception as e: + log.info(f'Error while calling the get_glue_database_from_catalog when resolving db name due to: {e}') + datasetGlueDatabase = GlueDatabaseName + old_shared_db_name = (datasetGlueDatabase + '_shared_' + shareUri)[:254] + database = GlueClient( + account_id=targetEnvAwsAccountId, database=old_shared_db_name, region=targetEnvRegion + ).get_glue_database() + return old_shared_db_name if database else datasetGlueDatabase + '_shared' diff --git a/backend/migrations/env.py b/backend/migrations/env.py index 7319d31fe..2816677cc 100644 --- a/backend/migrations/env.py +++ b/backend/migrations/env.py @@ -19,6 +19,7 @@ from dataall.modules.worksheets.db.worksheet_models import WorksheetQueryResult, Worksheet from dataall.modules.omics.db.omics_models import OmicsWorkflow, OmicsRun from dataall.modules.metadata_forms.db.metadata_form_models import * +from dataall.modules.redshift_datasets.db.redshift_models import RedshiftDataset, RedshiftTable, RedshiftConnection # fmt: on # enable ruff-format back diff --git a/backend/migrations/versions/852cdf6cf1e0_add_redshift_datasets.py b/backend/migrations/versions/852cdf6cf1e0_add_redshift_datasets.py new file mode 100644 index 000000000..3e7a38c35 --- /dev/null +++ b/backend/migrations/versions/852cdf6cf1e0_add_redshift_datasets.py @@ -0,0 +1,151 @@ +"""add_redshift_datasets + +Revision ID: 852cdf6cf1e0 +Revises: 7c5b30fee306 +Create Date: 2024-07-25 08:25:34.122091 + +""" + +from alembic import op +import sqlalchemy as sa +from sqlalchemy import orm +from sqlalchemy.dialects import postgresql + +from dataall.core.environment.db.environment_models import Environment +from dataall.core.permissions.services.resource_policy_service import ResourcePolicyService +from dataall.core.permissions.services.permission_service import PermissionService +from dataall.core.permissions.api.enums import PermissionType + + +# revision identifiers, used by Alembic. +revision = '852cdf6cf1e0' +down_revision = '7c5b30fee306' +branch_labels = None +depends_on = None + +# Redshift permissions +LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS = 'LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS' +CREATE_REDSHIFT_CONNECTION = 'CREATE_REDSHIFT_CONNECTION' +IMPORT_REDSHIFT_DATASET = 'IMPORT_REDSHIFT_DATASET' + +ENVIRONMENT_REDSHIFT_ALL = [LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS, CREATE_REDSHIFT_CONNECTION, IMPORT_REDSHIFT_DATASET] +ENVIRONMENT_REDSHIFT_ALL_WITH_DESC = {} +ENVIRONMENT_REDSHIFT_ALL_WITH_DESC[LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS] = 'LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS' +ENVIRONMENT_REDSHIFT_ALL_WITH_DESC[CREATE_REDSHIFT_CONNECTION] = 'Create Redshift Connection in this environment' +ENVIRONMENT_REDSHIFT_ALL_WITH_DESC[IMPORT_REDSHIFT_DATASET] = 'Import Redshift Datasets to this environment' + + +def upgrade(): + op.create_table( + 'redshift_connection', + sa.Column('label', sa.String(), nullable=False), + sa.Column('name', sa.String(), nullable=False), + sa.Column('owner', sa.String(), nullable=False), + sa.Column('created', sa.DateTime(), nullable=True), + sa.Column('updated', sa.DateTime(), nullable=True), + sa.Column('deleted', sa.DateTime(), nullable=True), + sa.Column('description', sa.String(), nullable=True), + sa.Column('tags', postgresql.ARRAY(sa.String()), nullable=True), + sa.Column('connectionUri', sa.String(), nullable=False), + sa.Column('environmentUri', sa.String(), nullable=False), + sa.Column('SamlGroupName', sa.String(), nullable=False), + sa.Column('redshiftType', sa.String(), nullable=False), + sa.Column('clusterId', sa.String(), nullable=True), + sa.Column('nameSpaceId', sa.String(), nullable=True), + sa.Column('workgroup', sa.String(), nullable=True), + sa.Column('database', sa.String(), nullable=False), + sa.Column('redshiftUser', sa.String(), nullable=True), + sa.Column('secretArn', sa.String(), nullable=True), + sa.ForeignKeyConstraint( + ['environmentUri'], + ['environment.environmentUri'], + ), + sa.PrimaryKeyConstraint('connectionUri'), + ) + + op.create_table( + 'redshift_dataset', + sa.Column('datasetUri', sa.String(), nullable=False), + sa.Column('connectionUri', sa.String(), nullable=False), + sa.Column('schema', sa.String(), nullable=False), + sa.ForeignKeyConstraint( + ['connectionUri'], + ['redshift_connection.connectionUri'], + ), + sa.ForeignKeyConstraint( + ['datasetUri'], + ['dataset.datasetUri'], + ), + sa.PrimaryKeyConstraint('datasetUri'), + ) + + op.create_table( + 'redshift_table', + sa.Column('label', sa.String(), nullable=False), + sa.Column('name', sa.String(), nullable=False), + sa.Column('owner', sa.String(), nullable=False), + sa.Column('created', sa.DateTime(), nullable=True), + sa.Column('updated', sa.DateTime(), nullable=True), + sa.Column('deleted', sa.DateTime(), nullable=True), + sa.Column('description', sa.String(), nullable=True), + sa.Column('tags', postgresql.ARRAY(sa.String()), nullable=True), + sa.Column('datasetUri', sa.String(), nullable=False), + sa.Column('rsTableUri', sa.String(), nullable=False), + sa.Column('topics', postgresql.ARRAY(sa.String()), nullable=True), + sa.ForeignKeyConstraint(['datasetUri'], ['redshift_dataset.datasetUri'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('rsTableUri'), + ) + try: + op.execute("ALTER TYPE datasettypes ADD VALUE 'Redshift'") + except Exception as e: + if 'already exists' in str(e): + pass + else: + raise e + + ## Backfilling Redshift permissions + # First we need to create the permissions as save_perms runs after the migrations + bind = op.get_bind() + session = orm.Session(bind=bind) + for perm in ENVIRONMENT_REDSHIFT_ALL: + PermissionService.save_permission( + session, + name=perm, + description=ENVIRONMENT_REDSHIFT_ALL_WITH_DESC[perm], + permission_type=PermissionType.RESOURCE.name, + ) + all_environments = session.query(Environment).all() + for env in all_environments: + ResourcePolicyService.attach_resource_policy( + session=session, + group=env.SamlGroupName, + resource_uri=env.environmentUri, + permissions=ENVIRONMENT_REDSHIFT_ALL, + resource_type=Environment.__name__, + ) + + +def downgrade(): + op.drop_table('redshift_table') + op.drop_table('redshift_dataset') + op.drop_table('redshift_connection') + # There is no postgres command to DELETE VALUE from an enum + # In the official docs is recommended to leave it: + # https://www.postgresql.org/message-id/21012.1459434338%40sss.pgh.pa.us + + # Deleting Redshift permissions + bind = op.get_bind() + session = orm.Session(bind=bind) + all_environments = session.query(Environment).all() + for env in all_environments: + policies = ResourcePolicyService.find_resource_policies( + session=session, + group=env.SamlGroupName, + resource_uri=env.environmentUri, + resource_type=Environment.__name__, + permissions=ENVIRONMENT_REDSHIFT_ALL, + ) + for policy in policies: + for permission in policy.permissions: + session.delete(permission) + session.commit() diff --git a/backend/requirements.txt b/backend/requirements.txt index 5fccee041..e0a34d519 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,7 +1,7 @@ ariadne==0.17.0 aws-xray-sdk==2.4.3 -boto3==1.28.23 -botocore==1.31.23 +boto3==1.34.119 +botocore==1.34.119 fastapi == 0.109.2 Flask==3.0.3 flask-cors==4.0.1 diff --git a/config.json b/config.json index 09d2b1ad5..3027bf95a 100644 --- a/config.json +++ b/config.json @@ -46,6 +46,9 @@ "s3_datasets_shares": { "active": true }, + "redshift_datasets": { + "active": true + }, "worksheets": { "active": true }, diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8dd0e6c5f..1062faf1b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -4584,27 +4584,6 @@ "node": ">= 12.0.0" } }, - "node_modules/@aws-sdk/client-sts/node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, "node_modules/@aws-sdk/client-sts/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -19537,9 +19516,9 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "node_modules/fast-xml-parser": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.0.tgz", - "integrity": "sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", "funding": [ { "type": "github", diff --git a/frontend/package.json b/frontend/package.json index a9996a427..1c73268fd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -93,7 +93,8 @@ "follow-redirects": "1.15.6", "webpack-dev-middleware": "5.3.4", "express": "4.19.2", - "ejs": "3.1.10" + "ejs": "3.1.10", + "fast-xml-parser": "4.4.1" }, "resolutions": { "react-redux": "^7.2.6", @@ -108,7 +109,8 @@ "webpack-dev-middleware": "5.3.4", "express": "4.19.2", "ejs": "3.1.10", - "ws": "^8.17.1" + "ws": "^8.17.1", + "fast-xml-parser": "4.4.1" }, "devDependencies": { "env-cmd": "^10.1.0", diff --git a/frontend/public/static/icons/amazon-s3.svg b/frontend/public/static/icons/amazon-s3.svg new file mode 100644 index 000000000..cd203eaad --- /dev/null +++ b/frontend/public/static/icons/amazon-s3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/static/icons/aws-redshift.svg b/frontend/public/static/icons/aws-redshift.svg new file mode 100644 index 000000000..364155af6 --- /dev/null +++ b/frontend/public/static/icons/aws-redshift.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/modules/Catalog/components/GlossarySearchResultItem.js b/frontend/src/modules/Catalog/components/GlossarySearchResultItem.js index 7b624c53a..0b94b91e8 100644 --- a/frontend/src/modules/Catalog/components/GlossarySearchResultItem.js +++ b/frontend/src/modules/Catalog/components/GlossarySearchResultItem.js @@ -27,10 +27,18 @@ import { RequestDashboardAccessModal } from './RequestDashboardAccessModal'; const HitICon = ({ hit }) => ( - + } /> - + } /> @@ -127,6 +135,28 @@ export const GlossarySearchResultItem = ({ hit }) => { {hit.label} )} + {hit.resourceKind === 'redshiftdataset' && ( + + {hit.label} + + )} + {hit.resourceKind === 'redshifttable' && ( + + {hit.label} + + )} by{' '} @@ -279,7 +309,9 @@ export const GlossarySearchResultItem = ({ hit }) => { }} > - {isOpeningModal || isOpeningDashboardModal ? ( + {hit.resourceKind === 'redshiftdataset' || + hit.resourceKind === 'redshifttable' ? null : isOpeningModal || + isOpeningDashboardModal ? ( ) : ( + + + + )} diff --git a/frontend/src/modules/DatasetsBase/components/DatasetGovernance.js b/frontend/src/modules/DatasetsBase/components/DatasetGovernance.js new file mode 100644 index 000000000..e484636ef --- /dev/null +++ b/frontend/src/modules/DatasetsBase/components/DatasetGovernance.js @@ -0,0 +1,130 @@ +import { + Box, + Card, + CardContent, + CardHeader, + Chip, + Divider, + Grid, + Typography +} from '@mui/material'; +import PropTypes from 'prop-types'; +import { Label } from 'design'; +import { isFeatureEnabled } from 'utils'; + +export const DatasetGovernance = (props) => { + const { dataset } = props; + const terms = + dataset.terms.nodes.length > 0 + ? dataset.terms.nodes + : [{ label: '-', nodeUri: '-' }]; + const tags = dataset.tags.length > 0 ? dataset.tags : ['-']; + + return ( + + + + + + {isFeatureEnabled('datasets_base', 'confidentiality_dropdown') && ( + + + Confidentiality + + + + + + )} + {isFeatureEnabled('datasets_base', 'topics_dropdown') && ( + + + Topics + + + {dataset.topics && + dataset.topics.length > 0 && + dataset.topics.map((t) => ( + + ))} + + + )} + + + + Tags + + + {tags && + tags.map((t) => ( + + ))} + + + + + Glossary terms + + + {terms && + terms.map((term) => ( + + ))} + + + + + + + + + + + Owners + + + {dataset.SamlAdminGroupName} + + + + + Stewards + + + {dataset.stewards} + + + + + Auto-Approval + + + + + + + + + ); +}; + +DatasetGovernance.propTypes = { + dataset: PropTypes.object.isRequired +}; diff --git a/frontend/src/modules/DatasetsBase/components/DatasetListItem.js b/frontend/src/modules/DatasetsBase/components/DatasetListItem.js index 85726a435..23cdc5d87 100644 --- a/frontend/src/modules/DatasetsBase/components/DatasetListItem.js +++ b/frontend/src/modules/DatasetsBase/components/DatasetListItem.js @@ -1,4 +1,5 @@ import { + Avatar, Box, Button, Card, @@ -14,10 +15,22 @@ import * as FaIcons from 'react-icons/fa'; import * as FiIcons from 'react-icons/fi'; import { useNavigate } from 'react-router'; import { Link as RouterLink } from 'react-router-dom'; -import { IconAvatar, Label, StackStatus, useCardStyle } from 'design'; +import { Label, StackStatus, useCardStyle } from 'design'; export const DatasetListItem = (props) => { const { dataset } = props; + const datasetTypeLink = + dataset.datasetType === 'DatasetTypes.S3' + ? `s3-datasets` + : dataset.datasetType === 'DatasetTypes.Redshift' + ? `redshift-datasets` + : '-'; + const datasetTypeIcon = + dataset.datasetType === 'DatasetTypes.S3' + ? `/static/icons/amazon-s3.svg` + : dataset.datasetType === 'DatasetTypes.Redshift' + ? `/static/icons/aws-redshift.svg` + : '-'; const classes = useCardStyle(); const navigate = useNavigate(); return ( @@ -32,7 +45,7 @@ export const DatasetListItem = (props) => { display: 'flex' }} > - } /> + { variant="h6" onClick={() => { navigate( - dataset.datasetType === 'DatasetTypes.S3' - ? `/console/s3-datasets/${dataset.datasetUri}` + datasetTypeLink + ? `/console/${datasetTypeLink}/${dataset.datasetUri}` : '-' ); }} @@ -57,7 +70,12 @@ export const DatasetListItem = (props) => { }} > - {dataset.label} + {dataset.datasetType === 'DatasetTypes.S3' + ? `S3/Glue: ` + : dataset.datasetType === 'DatasetTypes.Redshift' + ? `Redshift: ` + : '-'} + {dataset.label} @@ -159,20 +177,22 @@ export const DatasetListItem = (props) => { py: 0.5 }} > - - - - Status - + {dataset.stack && dataset.stack.status && ( + + + + Status + + + + + + + - - - - - - + )} { diff --git a/frontend/src/modules/DatasetsBase/components/index.js b/frontend/src/modules/DatasetsBase/components/index.js index 8d69e525d..2010fb826 100644 --- a/frontend/src/modules/DatasetsBase/components/index.js +++ b/frontend/src/modules/DatasetsBase/components/index.js @@ -1,2 +1,3 @@ export * from './DatasetCreateWindow'; +export * from './DatasetGovernance'; export * from './DatasetListItem'; diff --git a/frontend/src/modules/DatasetsBase/index.js b/frontend/src/modules/DatasetsBase/index.js index cbf3f4b7f..57ad639e1 100644 --- a/frontend/src/modules/DatasetsBase/index.js +++ b/frontend/src/modules/DatasetsBase/index.js @@ -5,6 +5,9 @@ export const DatasetsBaseModule = { name: 'datasets_base', isEnvironmentModule: false, resolve_dependency: () => { - return getModuleActiveStatus(ModuleNames.S3_DATASETS); // Add other dataset types when needed + return ( + getModuleActiveStatus(ModuleNames.S3_DATASETS) || + getModuleActiveStatus(ModuleNames.REDSHIFT_DATASETS) + ); } }; diff --git a/frontend/src/modules/Environments/components/EnvironmentOwnedDatasets.js b/frontend/src/modules/Environments/components/EnvironmentOwnedDatasets.js index 5ef920184..cf03bd568 100644 --- a/frontend/src/modules/Environments/components/EnvironmentOwnedDatasets.js +++ b/frontend/src/modules/Environments/components/EnvironmentOwnedDatasets.js @@ -123,6 +123,7 @@ export const EnvironmentOwnedDatasets = ({ environment }) => { Name + Dataset type Creator Owners Status @@ -137,6 +138,13 @@ export const EnvironmentOwnedDatasets = ({ environment }) => { items.nodes.map((dataset) => ( {dataset.label} + + {dataset.datasetType === 'DatasetTypes.S3' + ? `S3/Glue Dataset` + : dataset.datasetType === 'DatasetTypes.Redshift' + ? `Redshift Dataset` + : '-'} + {dataset.owner} {dataset.SamlAdminGroupName} @@ -149,9 +157,16 @@ export const EnvironmentOwnedDatasets = ({ environment }) => { { - navigate( + let datasetTypeLink = dataset.datasetType === 'DatasetTypes.S3' - ? `/console/s3-datasets/${dataset.datasetUri}` + ? `s3-datasets` + : dataset.datasetType === + 'DatasetTypes.Redshift' + ? `redshift-datasets` + : '-'; + navigate( + datasetTypeLink + ? `/console/${datasetTypeLink}/${dataset.datasetUri}` : '-' ); }} diff --git a/frontend/src/modules/Environments/components/EnvironmentRedshiftConnectionAddForm.js b/frontend/src/modules/Environments/components/EnvironmentRedshiftConnectionAddForm.js new file mode 100644 index 000000000..9dd0b28fc --- /dev/null +++ b/frontend/src/modules/Environments/components/EnvironmentRedshiftConnectionAddForm.js @@ -0,0 +1,365 @@ +import { GroupAddOutlined } from '@mui/icons-material'; +import { LoadingButton } from '@mui/lab'; +import { + Autocomplete, + Box, + CardContent, + CircularProgress, + Dialog, + Divider, + Grid, + MenuItem, + TextField, + Typography +} from '@mui/material'; +import { Formik } from 'formik'; +import { useSnackbar } from 'notistack'; +import PropTypes from 'prop-types'; +import React from 'react'; +import * as Yup from 'yup'; +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { useClient, useFetchGroups } from 'services'; +import { createRedshiftConnection } from '../services'; + +export const EnvironmentRedshiftConnectionAddForm = (props) => { + const { environment, onClose, open, reload, ...other } = props; + const { enqueueSnackbar } = useSnackbar(); + const dispatch = useDispatch(); + const client = useClient(); + + let { groupOptions, loadingGroups } = useFetchGroups(environment); + + const clusterOptions = [ + { value: 'serverless', label: 'Serverless' }, + { value: 'cluster', label: 'Provisioned Cluster' } + ]; + + async function submit(values, setStatus, setSubmitting, setErrors) { + try { + const response = await client.mutate( + createRedshiftConnection({ + connectionName: values.connectionName, + SamlGroupName: values.SamlAdminGroupName, + environmentUri: environment.environmentUri, + redshiftType: values.redshiftType, + clusterId: values.clusterId, + nameSpaceId: values.nameSpaceId, + workgroup: values.workgroup, + database: values.database, + redshiftUser: values.redshiftUser, + secretArn: values.secretArn + }) + ); + if (!response.errors) { + setStatus({ success: true }); + setSubmitting(false); + enqueueSnackbar('Redshift connection added to environment', { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + }); + if (reload) { + reload(); + } + if (onClose) { + onClose(); + } + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (err) { + console.error(err); + setStatus({ success: false }); + setErrors({ submit: err.message }); + setSubmitting(false); + dispatch({ type: SET_ERROR, error: err.message }); + } + } + + if (!environment) { + return null; + } + + if (loadingGroups) { + return ; + } + + return ( + + + + Add a Redshift connection to environment {environment.label} + + + The Redshift connection is owned by the selected Team. It is used to + import Redshift Datasets. + + + { + await submit(values, setStatus, setSubmitting, setErrors); + }} + > + {({ + errors, + handleChange, + handleSubmit, + isSubmitting, + setFieldValue, + touched, + values + }) => ( +
+ + + + + + option)} + noOptionsText="No teams found for this environment" + onChange={(event, value) => { + if (value && value.value) { + setFieldValue('SamlAdminGroupName', value.value); + } else { + setFieldValue('SamlAdminGroupName', ''); + } + }} + renderInput={(params) => ( + + )} + /> + + + + + + + {clusterOptions.map((r) => ( + + {r.label} + + ))} + + + + + {values.redshiftType === 'serverless' && ( + + + + + + + + + )} + {values.redshiftType === 'cluster' && ( + + + + + + )} + + + + + + + + You can choose to provide a Redshift user (for Provisioned + Cluster) or a Secrets Manager secret. + + + {values.redshiftType !== 'serverless' && ( + + + + + OR + + )} + + + + + + } + color="primary" + disabled={isSubmitting} + type="submit" + variant="contained" + > + Add Connection + + + +
+ )} +
+
+
+
+ ); +}; + +EnvironmentRedshiftConnectionAddForm.propTypes = { + environment: PropTypes.object.isRequired, + onClose: PropTypes.func, + open: PropTypes.bool.isRequired, + reload: PropTypes.func +}; diff --git a/frontend/src/modules/Environments/components/EnvironmentRedshiftConnections.js b/frontend/src/modules/Environments/components/EnvironmentRedshiftConnections.js new file mode 100644 index 000000000..76c7e057a --- /dev/null +++ b/frontend/src/modules/Environments/components/EnvironmentRedshiftConnections.js @@ -0,0 +1,378 @@ +import { + Box, + Button, + Card, + CardHeader, + CircularProgress, + Divider, + Grid, + InputAdornment, + TextField +} from '@mui/material'; +import { DataGrid, GridActionsCellItem, GridRowModes } from '@mui/x-data-grid'; +import { + GroupAddOutlined, + SupervisedUserCircleRounded +} from '@mui/icons-material'; +import PropTypes from 'prop-types'; +import React, { useCallback, useEffect, useState } from 'react'; + +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { listEnvironmentRedshiftConnections, useClient } from 'services'; +import { + Defaults, + DeleteObjectWithFrictionModal, + RefreshTableMenu, + Scrollbar, + SearchIcon +} from 'design'; + +import { EnvironmentRedshiftConnectionAddForm } from './EnvironmentRedshiftConnectionAddForm'; +import { deleteRedshiftConnection } from '../services'; +import SaveIcon from '@mui/icons-material/Save'; +import CancelIcon from '@mui/icons-material/Close'; +import DeleteIcon from '@mui/icons-material/DeleteOutlined'; +import { useSnackbar } from 'notistack'; + +export const EnvironmentRedshiftConnections = ({ environment }) => { + const client = useClient(); + const dispatch = useDispatch(); + const { enqueueSnackbar } = useSnackbar(); + const [loading, setLoading] = useState(true); + const [items, setItems] = useState(Defaults.pagedResponse); + const [filter, setFilter] = useState(Defaults.filter); + const [inputValue, setInputValue] = useState(''); + const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); + const [rowModesModel, setRowModesModel] = useState({}); + const [isDeleteModalOpenId, setIsDeleteModalOpen] = useState(0); + + const handleInputChange = (event) => { + setInputValue(event.target.value); + setFilter({ ...filter, term: event.target.value }); + }; + + const handleInputKeyup = (event) => { + if (event.code === 'Enter') { + fetchItems().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + } + }; + const handleCreateModalOpen = () => { + setIsCreateModalOpen(true); + }; + + const handleCreateModalClose = () => { + setIsCreateModalOpen(false); + }; + + const handlePageChange = async (page) => { + page += 1; //expecting 1-indexing + if (page <= items.pages && page !== items.page) { + await setFilter({ ...filter, page: page }); + } + }; + + const handleSaveClick = (id) => () => { + setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } }); + }; + + const handleCancelClick = (id) => () => { + setRowModesModel({ + ...rowModesModel, + [id]: { mode: GridRowModes.View, ignoreModifications: true } + }); + }; + + const handleRowEditStart = (params, event) => { + event.defaultMuiPrevented = true; + }; + + const handleRowEditStop = (params, event) => { + event.defaultMuiPrevented = true; + }; + + const handleDeleteModalOpen = (id) => { + setIsDeleteModalOpen(id); + }; + const handleDeleteModalClosed = (id) => { + setIsDeleteModalOpen(0); + }; + + const deleteConnection = async (connectionUri) => { + try { + const response = await client.mutate( + deleteRedshiftConnection({ + connectionUri: connectionUri + }) + ); + if (!response.errors) { + enqueueSnackbar('Redshift connection removed from environment', { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + }); + fetchItems(); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (e) { + dispatch({ type: SET_ERROR, error: e.message }); + } + }; + + const fetchItems = useCallback(async () => { + try { + const response = await client.query( + listEnvironmentRedshiftConnections({ + filter: { ...filter, environmentUri: environment.environmentUri } + }) + ); + if (!response.errors) { + setItems({ + ...response.data.listEnvironmentRedshiftConnections, + nodes: [ + ...response.data.listEnvironmentRedshiftConnections.nodes.map( + (item) => ({ + ...item, + id: item.connectionUri, + redshiftId: + item.redshiftType === 'serverless' + ? item.nameSpaceId + : item.clusterId, + connectionType: item.secretArn ? 'SecretArn' : 'Redshift User', + connectionDetails: item.secretArn + ? item.secretArn + : item.redshiftUser, + workgroup: item.workgroup ? item.workgroup : '-' + }) + ) + ] + }); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (e) { + dispatch({ type: SET_ERROR, error: e.message }); + } finally { + setLoading(false); + } + }, [client, dispatch, filter, environment.environmentUri]); + + useEffect(() => { + if (client) { + fetchItems().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + } + }, [client, filter.page, fetchItems, dispatch]); + + if (loading) { + return ; + } + + return ( + + + + } + title={ + + {' '} + Redshift Connections + + } + /> + + + + + + + + ) + }} + onChange={handleInputChange} + onKeyUp={handleInputKeyup} + placeholder="Search" + value={inputValue} + variant="outlined" + /> + + + + + {isCreateModalOpen && ( + + )} + + + + + node.connectionUri} + rows={items.nodes} + columns={[ + { field: 'id', hide: true }, + { + field: 'name', + headerName: 'Name', + flex: 0.5, + editable: true + }, + { + field: 'SamlGroupName', + headerName: 'Team', + flex: 1, + editable: false + }, + { + field: 'redshiftType', + headerName: 'Redshift Type', + flex: 1, + editable: false + }, + { + field: 'redshiftId', + headerName: 'NamespaceId/ClusterId', + flex: 1, + editable: false + }, + { + field: 'workgroup', + headerName: 'Workgroup', + flex: 1, + editable: false + }, + { + field: 'connectionType', + headerName: 'Connection Type', + flex: 1, + editable: false + }, + { + field: 'database', + headerName: 'Database', + flex: 1, + editable: false + }, + { + field: 'connectionDetails', + headerName: 'SecretArn/Redshift user', + flex: 1, + editable: false + }, + { + field: 'actions', + headerName: 'Actions', + flex: 0.5, + type: 'actions', + cellClassName: 'actions', + getActions: ({ id, ...props }) => { + const name = props.row.name; + const isInEditMode = + rowModesModel[id]?.mode === GridRowModes.Edit; + + if (isInEditMode) { + return [ + } + label="Save" + sx={{ + color: 'primary.main' + }} + onClick={handleSaveClick(id)} + />, + } + label="Cancel" + className="textPrimary" + onClick={handleCancelClick(id)} + color="inherit" + /> + ]; + } + return [ + } + label="Delete" + onClick={() => handleDeleteModalOpen(id)} + color="inherit" + />, + handleDeleteModalClosed(id)} + onClose={() => handleDeleteModalClosed(id)} + open={isDeleteModalOpenId === id} + isAWSResource={false} + deleteFunction={() => deleteConnection(id)} + /> + ]; + } + } + ]} + editMode="row" + rowModesModel={rowModesModel} + onRowModesModelChange={setRowModesModel} + onRowEditStart={handleRowEditStart} + onRowEditStop={handleRowEditStop} + experimentalFeatures={{ newEditingApi: true }} + rowCount={items.count} + page={items.page - 1} + pageSize={filter.pageSize} + paginationMode="server" + onPageChange={handlePageChange} + loading={loading} + onPageSizeChange={(pageSize) => { + setFilter({ ...filter, pageSize: pageSize }); + }} + getRowHeight={() => 'auto'} + disableSelectionOnClick + sx={{ wordWrap: 'break-word' }} + /> + + + + + + ); +}; + +EnvironmentRedshiftConnections.propTypes = { + environment: PropTypes.object.isRequired +}; diff --git a/frontend/src/modules/Environments/components/index.js b/frontend/src/modules/Environments/components/index.js index 7aecd51fa..5c7379322 100644 --- a/frontend/src/modules/Environments/components/index.js +++ b/frontend/src/modules/Environments/components/index.js @@ -1,3 +1,4 @@ +export * from './EnvironmentRedshiftConnections'; export * from './EnvironmentConsoleAccess'; export * from './EnvironmentDatasets'; export * from './EnvironmentFeatures'; diff --git a/frontend/src/modules/Environments/services/createRedshiftConnection.js b/frontend/src/modules/Environments/services/createRedshiftConnection.js new file mode 100644 index 000000000..6d37e0041 --- /dev/null +++ b/frontend/src/modules/Environments/services/createRedshiftConnection.js @@ -0,0 +1,14 @@ +import { gql } from 'apollo-boost'; + +export const createRedshiftConnection = (input) => ({ + variables: { + input + }, + mutation: gql` + mutation createRedshiftConnection($input: CreateRedshiftConnectionInput) { + createRedshiftConnection(input: $input) { + connectionUri + } + } + ` +}); diff --git a/frontend/src/modules/Environments/services/deleteRedshiftConnection.js b/frontend/src/modules/Environments/services/deleteRedshiftConnection.js new file mode 100644 index 000000000..9c1720553 --- /dev/null +++ b/frontend/src/modules/Environments/services/deleteRedshiftConnection.js @@ -0,0 +1,12 @@ +import { gql } from 'apollo-boost'; + +export const deleteRedshiftConnection = ({ connectionUri }) => ({ + variables: { + connectionUri + }, + mutation: gql` + mutation deleteRedshiftConnection($connectionUri: String!) { + deleteRedshiftConnection(connectionUri: $connectionUri) + } + ` +}); diff --git a/frontend/src/modules/Environments/services/index.js b/frontend/src/modules/Environments/services/index.js index e52b10a61..4151f0d7c 100644 --- a/frontend/src/modules/Environments/services/index.js +++ b/frontend/src/modules/Environments/services/index.js @@ -2,6 +2,8 @@ export * from './addConsumptionRoleToEnvironment'; export * from './archiveEnvironment'; export * from './createEnvironment'; export * from './createNetwork'; +export * from './createRedshiftConnection'; +export * from './deleteRedshiftConnection'; export * from './deleteNetwork'; export * from './disableDataSubscriptions'; export * from './enableDataSubscriptions'; diff --git a/frontend/src/modules/Environments/views/EnvironmentView.js b/frontend/src/modules/Environments/views/EnvironmentView.js index 1ac3e4629..42d726c3b 100644 --- a/frontend/src/modules/Environments/views/EnvironmentView.js +++ b/frontend/src/modules/Environments/views/EnvironmentView.js @@ -38,6 +38,7 @@ import { useClient } from 'services'; import { archiveEnvironment, getEnvironment } from '../services'; import { KeyValueTagList, Stack } from 'modules/Shared'; import { + EnvironmentRedshiftConnections, EnvironmentDatasets, EnvironmentMLStudio, EnvironmentOverview, @@ -58,7 +59,15 @@ const tabs = [ label: 'Datasets', value: 'datasets', icon: , - active: isModuleEnabled(ModuleNames.S3_DATASETS) + active: isModuleEnabled( + ModuleNames.S3_DATASETS || ModuleNames.REDSHIFT_DATASETS + ) + }, + { + label: 'Connections', + value: 'connections', + icon: , + active: isModuleEnabled(ModuleNames.REDSHIFT_DATASETS) }, { label: 'ML Studio Domain', @@ -258,6 +267,9 @@ const EnvironmentView = () => { {currentTab === 'datasets' && ( )} + {currentTab === 'connections' && ( + + )} {currentTab === 'networks' && ( )} diff --git a/frontend/src/modules/Glossaries/components/GlossaryAssociations.js b/frontend/src/modules/Glossaries/components/GlossaryAssociations.js index 8db3303e6..373ee70f6 100644 --- a/frontend/src/modules/Glossaries/components/GlossaryAssociations.js +++ b/frontend/src/modules/Glossaries/components/GlossaryAssociations.js @@ -138,17 +138,25 @@ export const GlossaryAssociations = ({ glossary }) => { {/* eslint-disable-next-line no-underscore-dangle */} {item.targetType === 'Dataset' && ( - Dataset + S3/Glue Dataset )} {/* eslint-disable-next-line no-underscore-dangle */} {item.targetType === 'DatasetTable' && ( - Table + Glue Table )} {/* eslint-disable-next-line no-underscore-dangle */} {item.targetType === 'Folder' && Folder} {item.targetType === 'Dashboard' && ( Dashboard )} + {/* eslint-disable-next-line no-underscore-dangle */} + {item.targetType === 'RedshiftDataset' && ( + Redshift Dataset + )} + {/* eslint-disable-next-line no-underscore-dangle */} + {item.targetType === 'RedshiftDatasetTable' && ( + Redshift Table + )} {/* eslint-disable-next-line no-underscore-dangle */} @@ -199,6 +207,30 @@ export const GlossaryAssociations = ({ glossary }) => { {item.target.label} )} + {/* eslint-disable-next-line no-underscore-dangle */} + {item.targetType === 'RedshiftDataset' && ( + + {item.target.label} + + )} + {/* eslint-disable-next-line no-underscore-dangle */} + {item.targetType === 'RedshiftDatasetTable' && ( + + {item.target.label} + + )} {!item.approvedBySteward ? ( diff --git a/frontend/src/modules/Maintenance/components/MaintenanceViewer.js b/frontend/src/modules/Maintenance/components/MaintenanceViewer.js index 8fcf252f0..a4048dba9 100644 --- a/frontend/src/modules/Maintenance/components/MaintenanceViewer.js +++ b/frontend/src/modules/Maintenance/components/MaintenanceViewer.js @@ -27,16 +27,11 @@ import { startMaintenanceWindow, startReindexCatalog } from '../services'; -import { useClient } from 'services'; +import { useClient, fetchEnums } from 'services'; import { SET_ERROR, useDispatch } from 'globalErrors'; import { useSnackbar } from 'notistack'; import { ModuleNames, isModuleEnabled } from 'utils'; -const maintenanceModes = [ - { value: 'READ-ONLY', label: 'Read-Only' }, - { value: 'NO-ACCESS', label: 'No-Access' } -]; - const START_MAINTENANCE = 'Start Maintenance'; const END_MAINTENANCE = 'End Maintenance'; export const PENDING_STATUS = 'PENDING'; @@ -314,11 +309,25 @@ export const MaintenanceViewer = () => { useState(START_MAINTENANCE); const [maintenanceWindowStatus, setMaintenanceWindowStatus] = useState(INACTIVE_STATUS); + const [maintenanceModes, setMaintenanceModes] = useState([]); const [dropDownStatus, setDropDownStatus] = useState(false); const [refreshingTimer, setRefreshingTimer] = useState(''); const { enqueueSnackbar, closeSnackbar } = useSnackbar(); const dispatch = useDispatch(); + const fetchMaintenanceModes = async () => { + const maintenanceModesEnum = await fetchEnums(client, ['MaintenanceModes']); + if (maintenanceModesEnum['MaintenanceModes'].length > 0) { + setMaintenanceModes( + maintenanceModesEnum['MaintenanceModes'].map((elem) => { + return { label: elem.value, value: elem.name }; + }) + ); + } else { + dispatch({ type: SET_ERROR, error: 'Could not fetch maintenance modes' }); + } + }; + const refreshMaintenanceView = async () => { setUpdating(true); setRefreshing(true); @@ -468,6 +477,9 @@ export const MaintenanceViewer = () => { initializeMaintenanceView().catch((e) => dispatch({ type: SET_ERROR, e }) ); + fetchMaintenanceModes().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); const setTimer = setInterval(() => { refreshStatus().catch((e) => dispatch({ type: SET_ERROR, error: e.message }) diff --git a/frontend/src/modules/Redshift_Datasets/components/AddTablesModal.js b/frontend/src/modules/Redshift_Datasets/components/AddTablesModal.js new file mode 100644 index 000000000..820c0dfe8 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/components/AddTablesModal.js @@ -0,0 +1,191 @@ +import PropTypes from 'prop-types'; +import { + Box, + Button, + CircularProgress, + Dialog, + Divider, + Typography +} from '@mui/material'; +import PostAddIcon from '@mui/icons-material/PostAdd'; +import { DataGrid, GridRowParams } from '@mui/x-data-grid'; +import React, { useCallback, useEffect, useState } from 'react'; + +import { useSnackbar } from 'notistack'; +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { Defaults, Scrollbar } from 'design'; +import { useClient } from 'services'; +import { + addRedshiftDatasetTables, + listRedshiftSchemaDatasetTables +} from '../services'; + +export const AddTablesModal = (props) => { + const { onClose, open, dataset } = props; + const client = useClient(); + const dispatch = useDispatch(); + const { enqueueSnackbar } = useSnackbar(); + const [loading, setLoading] = useState(false); + const [items, setItems] = useState(null); + const [selectedTables, setSelectedTables] = useState(null); + const [filter, setFilter] = useState(Defaults.filter); + + const fetchItems = useCallback(async () => { + setLoading(true); + try { + const response = await client.query( + listRedshiftSchemaDatasetTables({ + datasetUri: dataset.datasetUri + }) + ); + if (!response.errors) { + setItems(response.data.listRedshiftSchemaDatasetTables); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (e) { + dispatch({ type: SET_ERROR, error: e.message }); + } + setLoading(false); + }, [client, dispatch, dataset]); + + const addTables = async (selected_tables) => { + const response = await client.mutate( + addRedshiftDatasetTables({ + datasetUri: dataset.datasetUri, + tables: selected_tables + }) + ); + if (!response.errors) { + enqueueSnackbar('Tables added', { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + }); + fetchItems(); + setSelectedTables(null); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + }; + + useEffect(() => { + if (client && dataset) { + fetchItems().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + } + }, [client, fetchItems, dispatch, dataset]); + + const handlePageChange = async (page) => { + page += 1; //expecting 1-indexing + if (page <= items.pages && page !== items.page) { + await setFilter({ ...filter, page: page }); + } + }; + + if (loading) { + return ( + + + + Loading database tables + + + + + + + ); + } + if (!dataset || !items) { + return null; + } + return ( + + + + Add tables to dataset: {dataset.label} + + + + + + params.row.alreadyAdded === 'false' + } + getRowId={(node) => node.name} + rows={items} + columns={[ + { field: 'id', hide: true }, + { + field: 'name', + headerName: 'Redshift tables', + flex: 0.5, + editable: false + }, + { + field: 'alreadyAdded', + headerName: 'Already added', + flex: 0.2, + editable: false + } + ]} + pageSize={filter.pageSize} + rowsPerPageOptions={[filter.pageSize]} + onPageChange={handlePageChange} + loading={loading} + onPageSizeChange={(pageSize) => { + setFilter({ + ...filter, + pageSize: pageSize + }); + }} + getRowHeight={() => 'auto'} + disableSelectionOnClick + onSelectionModelChange={(newSelectionModel) => { + setSelectedTables(newSelectionModel); + }} + sx={{ + wordWrap: 'break-word', + '& .MuiDataGrid-row': { + borderBottom: '1px solid rgba(145, 158, 171, 0.24)' + }, + '& .MuiDataGrid-columnHeaders': { + borderBottom: 0.5 + } + }} + /> + + + + + + ); +}; +AddTablesModal.propTypes = { + onApply: PropTypes.func, + onClose: PropTypes.func, + open: PropTypes.bool.isRequired, + dataset: PropTypes.object.isRequired +}; diff --git a/frontend/src/modules/Redshift_Datasets/components/RedshiftDatasetAWSInfo.js b/frontend/src/modules/Redshift_Datasets/components/RedshiftDatasetAWSInfo.js new file mode 100644 index 000000000..7906663b7 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/components/RedshiftDatasetAWSInfo.js @@ -0,0 +1,82 @@ +import PropTypes from 'prop-types'; +import { + Card, + CardContent, + CardHeader, + Divider, + Typography +} from '@mui/material'; + +import React from 'react'; + +export const RedshiftDatasetAWSInfo = (props) => { + const { dataset } = props; + + return ( + + + + + + Account + + + {dataset.AwsAccountId} + + + + + Redshift Connection + + + {dataset.connection.label} + + + + + Redshift type + + + {dataset.connection.redshiftType === 'cluster' + ? 'Provisioned cluster' + : dataset.connection.redshiftType === 'serverless' + ? 'Redshift Serverless' + : 'Unknown'} + + + {dataset.connection.redshiftType === 'serverless' ? ( +
+ + + Namespace Id + + + {dataset.connection.nameSpaceId} + + + + + Workgroup Id + + + {dataset.connection.workgroup} + + +
+ ) : ( + + + Cluster Id + + + {dataset.connection.clusterId} + + + )} +
+ ); +}; + +RedshiftDatasetAWSInfo.propTypes = { + dataset: PropTypes.object.isRequired +}; diff --git a/frontend/src/modules/Redshift_Datasets/components/RedshiftDatasetOverview.js b/frontend/src/modules/Redshift_Datasets/components/RedshiftDatasetOverview.js new file mode 100644 index 000000000..ca0f8c992 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/components/RedshiftDatasetOverview.js @@ -0,0 +1,50 @@ +import { Box, Grid } from '@mui/material'; +import PropTypes from 'prop-types'; +import { ObjectBrief, ObjectMetadata } from 'design'; +import { DatasetGovernance } from 'modules/DatasetsBase/components/DatasetGovernance'; +import { RedshiftDatasetAWSInfo } from './RedshiftDatasetAWSInfo'; + +export const RedshiftDatasetOverview = (props) => { + const { dataset, isAdmin, ...other } = props; + + return ( + + + + + + + + + + + + + + + + + + + + + ); +}; + +RedshiftDatasetOverview.propTypes = { + dataset: PropTypes.object.isRequired, + isAdmin: PropTypes.bool.isRequired +}; diff --git a/frontend/src/modules/Redshift_Datasets/components/RedshiftDatasetTables.js b/frontend/src/modules/Redshift_Datasets/components/RedshiftDatasetTables.js new file mode 100644 index 000000000..371f247e8 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/components/RedshiftDatasetTables.js @@ -0,0 +1,346 @@ +import { DeleteOutlined, Warning } from '@mui/icons-material'; +import PostAddIcon from '@mui/icons-material/PostAdd'; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; +import { + Box, + Button, + Card, + CardContent, + CardHeader, + Divider, + Grid, + IconButton, + InputAdornment, + Link, + Table, + TableBody, + TableCell, + TableHead, + TableRow, + TextField, + Typography +} from '@mui/material'; +import CircularProgress from '@mui/material/CircularProgress'; +import { useSnackbar } from 'notistack'; +import PropTypes from 'prop-types'; +import React, { useCallback, useEffect, useState } from 'react'; +import { BsTable } from 'react-icons/bs'; +import { useNavigate } from 'react-router'; +import { Link as RouterLink } from 'react-router-dom'; +import { + ArrowRightIcon, + Defaults, + DeleteObjectModal, + Pager, + RefreshTableMenu, + Scrollbar, + SearchIcon +} from 'design'; +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { useClient } from 'services'; + +import { + deleteRedshiftDatasetTable, + listRedshiftDatasetTables +} from '../services'; + +import { AddTablesModal } from './AddTablesModal'; +import { TableSchemaModal } from './TableSchemaModal'; + +export const RedshiftDatasetTables = (props) => { + const { dataset, isAdmin } = props; + const client = useClient(); + const dispatch = useDispatch(); + const { enqueueSnackbar } = useSnackbar(); + const navigate = useNavigate(); + const [items, setItems] = useState(Defaults.pagedResponse); + const [filter, setFilter] = useState(Defaults.filter); + const [loading, setLoading] = useState(null); + const [inputValue, setInputValue] = useState(''); + const [isDeleteObjectModalOpen, setIsDeleteObjectModalOpen] = useState(false); + const [isTableSchemaModalOpen, setIsTableSchemaModalOpen] = useState(false); + const [tableToDelete, setTableToDelete] = useState(null); + const [tableToSee, setTableToSee] = useState(null); + const [isAddTablesModalOpen, setIsAddTablesModalOpen] = useState(false); + + const handleDeleteObjectModalOpen = (table) => { + setTableToDelete(table); + setIsDeleteObjectModalOpen(true); + }; + const handleDeleteObjectModalClose = () => { + setTableToDelete(null); + setIsDeleteObjectModalOpen(false); + }; + + const handleAddTablesModalOpen = () => { + setIsAddTablesModalOpen(true); + }; + const handleAddTablesModalClose = () => { + setIsAddTablesModalOpen(false); + fetchItems().catch((e) => dispatch({ type: SET_ERROR, error: e.message })); + }; + + const handleTableSchemaModalOpen = () => { + setIsTableSchemaModalOpen(true); + }; + const handleTableSchemaModalClose = () => { + setIsTableSchemaModalOpen(false); + }; + + const fetchItems = useCallback(async () => { + setLoading(true); + const response = await client.query( + listRedshiftDatasetTables({ + datasetUri: dataset.datasetUri, + filter: { ...filter } + }) + ); + if (!response.errors) { + setItems({ ...response.data.listRedshiftDatasetTables }); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + setLoading(false); + }, [dispatch, client, dataset, filter]); + + const deleteTable = async () => { + const response = await client.mutate( + deleteRedshiftDatasetTable({ + rsTableUri: tableToDelete.rsTableUri + }) + ); + if (!response.errors) { + handleDeleteObjectModalClose(); + enqueueSnackbar('Table deleted', { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + }); + fetchItems().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + }; + + useEffect(() => { + if (client) { + fetchItems().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + } + }, [client, filter.page, dispatch, fetchItems]); + + const handleInputChange = (event) => { + setInputValue(event.target.value); + setFilter({ ...filter, term: event.target.value }); + }; + + const handleInputKeyup = (event) => { + if (event.code === 'Enter') { + fetchItems().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + } + }; + + const handlePageChange = async (event, value) => { + if (value <= items.pages && value !== items.page) { + await setFilter({ ...filter, page: value }); + } + }; + + return ( + <> + + + + + + + Database + + + {dataset.connection.database} + + + + + Schema + + + {dataset.schema} + + + + + + + + } + title={ + + + Tables + + } + /> + + + + + + + ) + }} + onChange={handleInputChange} + onKeyUp={handleInputKeyup} + placeholder="Search" + value={inputValue} + variant="outlined" + /> + + + + {isAddTablesModalOpen && ( + + )} + + + + + + + + Name + Description + Actions + + + {loading ? ( + + ) : ( + + {items.nodes.length > 0 ? ( + items.nodes.map((table) => ( + + + + {table.name} + + + {table.description} + + + {isAdmin && ( + { + setTableToDelete(table); + handleDeleteObjectModalOpen(table); + }} + > + + + )} + { + navigate( + `/console/redshift-datasets/table/${table.rsTableUri}` + ); + }} + > + + + + + )) + ) : ( + + No tables found + + )} + + )} +
+ {!loading && items.nodes.length > 0 && ( + + )} +
+
+
+ {isAdmin && tableToDelete && ( + + + + Redshift Table will be deleted from data.all + catalog, but will still be available in Amazon Redshift. + + + + } + /> + )} + + + ); +}; + +RedshiftDatasetTables.propTypes = { + dataset: PropTypes.object.isRequired, + isAdmin: PropTypes.bool.isRequired +}; diff --git a/frontend/src/modules/Redshift_Datasets/components/TableColumns.js b/frontend/src/modules/Redshift_Datasets/components/TableColumns.js new file mode 100644 index 000000000..de8d89d8c --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/components/TableColumns.js @@ -0,0 +1,138 @@ +import { Box, Card, CircularProgress } from '@mui/material'; +import { DataGrid } from '@mui/x-data-grid'; +import * as PropTypes from 'prop-types'; +import React, { useCallback, useEffect, useState } from 'react'; +import { Defaults, Scrollbar } from 'design'; +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { useClient } from 'services'; +import { getRedshiftDatasetTableColumns } from '../services'; + +export const TableColumns = (props) => { + const { table } = props; + const dispatch = useDispatch(); + const client = useClient(); + const [loading, setLoading] = useState(true); + const [items, setItems] = useState(null); + const [filter, setFilter] = useState(Defaults.filter); + + const fetchItems = useCallback(async () => { + setLoading(true); + const response = await client.query( + getRedshiftDatasetTableColumns({ + rsTableUri: table.rsTableUri, + filter: filter + }) + ); + if ( + !response.errors && + response.data.getRedshiftDatasetTableColumns !== null + ) { + setItems(response.data.getRedshiftDatasetTableColumns); + } else { + const error = response.errors + ? response.errors[0].message + : 'Redshift table not found'; + dispatch({ type: SET_ERROR, error }); + } + setLoading(false); + }, [client, dispatch, table]); + + useEffect(() => { + if (client && table) { + fetchItems().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + } + }, [client, fetchItems, dispatch, table]); + + const handlePageChange = async (page) => { + page += 1; //expecting 1-indexing + if (page <= items.pages && page !== items.page) { + await setFilter({ ...filter, page: page }); + } + }; + + if (loading) { + return ; + } + + if (!table || !items) { + return null; + } + + return ( + + + + + node.name} + rows={items.nodes} + columns={[ + { field: 'id', hide: true }, + { + field: 'name', + headerName: 'Name', + flex: 1.5, + editable: false + }, + { + field: 'typeName', + headerName: 'Type', + flex: 1, + editable: false + }, + { + field: 'length', + headerName: 'Length', + flex: 1, + editable: false + }, + { + field: 'nullable', + headerName: 'Nullable', + flex: 1, + editable: false + }, + { + field: 'columnDefault', + headerName: 'Default value', + flex: 1, + editable: false + } + ]} + rowCount={items.count} + page={items.page - 1} + pageSize={filter.pageSize} + paginationMode="server" + onPageChange={handlePageChange} + loading={loading} + onPageSizeChange={(pageSize) => { + setFilter({ ...filter, pageSize: pageSize }); + }} + getRowHeight={() => 'auto'} + disableSelectionOnClick + sx={{ + wordWrap: 'break-word', //TODO: create a generic styled datagrid to be used across features + '& .MuiDataGrid-row': { + borderBottom: '1px solid rgba(145, 158, 171, 0.24)' + }, + '& .MuiDataGrid-columnHeaders': { + borderBottom: 0.5 + }, + '&.MuiDataGrid-root--densityStandard .MuiDataGrid-cell': { + py: '15px' + } + }} + /> + + + + + ); +}; + +TableColumns.propTypes = { + table: PropTypes.object.isRequired +}; diff --git a/frontend/src/modules/Redshift_Datasets/components/TableOverview.js b/frontend/src/modules/Redshift_Datasets/components/TableOverview.js new file mode 100644 index 000000000..df17bb57d --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/components/TableOverview.js @@ -0,0 +1,43 @@ +import { Box, Grid } from '@mui/material'; +import PropTypes from 'prop-types'; +import { ObjectBrief, ObjectMetadata } from 'design'; + +export const TableOverview = (props) => { + const { table, ...other } = props; + + return ( + + + + 0 ? table.tags : ['-']} + terms={ + table.terms && table.terms.nodes.length > 0 + ? table.terms.nodes + : [{ label: '-', nodeUri: '-' }] + } + /> + + + + + + + ); +}; + +TableOverview.propTypes = { + table: PropTypes.object.isRequired +}; diff --git a/frontend/src/modules/Redshift_Datasets/components/TableSchemaModal.js b/frontend/src/modules/Redshift_Datasets/components/TableSchemaModal.js new file mode 100644 index 000000000..ab475b7a7 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/components/TableSchemaModal.js @@ -0,0 +1,162 @@ +import PropTypes from 'prop-types'; +import { + Box, + CircularProgress, + Dialog, + Divider, + Typography +} from '@mui/material'; +import React, { useCallback, useEffect, useState } from 'react'; + +import { getRedshiftDatasetTableColumns } from '../services'; +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { useClient } from 'services'; +import { DataGrid } from '@mui/x-data-grid'; +import { Defaults, Scrollbar } from 'design'; + +export const TableSchemaModal = (props) => { + const { onClose, open, table } = props; + const client = useClient(); + const dispatch = useDispatch(); + const [loading, setLoading] = useState(false); + const [items, setItems] = useState(null); + const [filter, setFilter] = useState(Defaults.filter); + + const fetchItems = useCallback(async () => { + setLoading(true); + const response = await client.query( + getRedshiftDatasetTableColumns({ + rsTableUri: table.rsTableUri, + filter: filter + }) + ); + if ( + !response.errors && + response.data.getRedshiftDatasetTableColumns !== null + ) { + setItems(response.data.getRedshiftDatasetTableColumns); + } else { + const error = response.errors + ? response.errors[0].message + : 'Redshift table not found'; + dispatch({ type: SET_ERROR, error }); + } + setLoading(false); + }, [client, dispatch, table]); + + useEffect(() => { + if (client && table) { + fetchItems().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + } + }, [client, fetchItems, dispatch, table]); + + const handlePageChange = async (page) => { + page += 1; //expecting 1-indexing + if (page <= items.pages && page !== items.page) { + await setFilter({ ...filter, page: page }); + } + }; + + if (loading) { + return ( + + + + Loading table schema + + + + + + + ); + } + if (!table || !items) { + return null; + } + return ( + + + + Redshift table: {table.label} + + + + + node.name} + rows={items.nodes} + columns={[ + { field: 'id', hide: true }, + { + field: 'name', + headerName: 'Name', + flex: 1.5, + editable: false + }, + { + field: 'typeName', + headerName: 'Type', + flex: 1, + editable: false + }, + { + field: 'length', + headerName: 'Length', + flex: 1, + editable: false + }, + { + field: 'nullable', + headerName: 'Nullable', + flex: 1, + editable: false + }, + { + field: 'columnDefault', + headerName: 'Default value', + flex: 1, + editable: false + } + ]} + rowCount={items.count} + page={items.page - 1} + pageSize={filter.pageSize} + paginationMode="server" + onPageChange={handlePageChange} + loading={loading} + onPageSizeChange={(pageSize) => { + setFilter({ ...filter, pageSize: pageSize }); + }} + getRowHeight={() => 'auto'} + disableSelectionOnClick + sx={{ + wordWrap: 'break-word', //TODO: create a generic styled datagrid to be used across features + '& .MuiDataGrid-row': { + borderBottom: '1px solid rgba(145, 158, 171, 0.24)' + }, + '& .MuiDataGrid-columnHeaders': { + borderBottom: 0.5 + } + }} + /> + + + + + ); +}; +TableSchemaModal.propTypes = { + onApply: PropTypes.func, + onClose: PropTypes.func, + open: PropTypes.bool.isRequired, + table: PropTypes.object.isRequired +}; diff --git a/frontend/src/modules/Redshift_Datasets/components/index.js b/frontend/src/modules/Redshift_Datasets/components/index.js new file mode 100644 index 000000000..c22707a58 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/components/index.js @@ -0,0 +1,5 @@ +export * from './RedshiftDatasetAWSInfo'; +export * from './RedshiftDatasetOverview'; +export * from './RedshiftDatasetTables'; +export * from './TableColumns'; +export * from './TableOverview'; diff --git a/frontend/src/modules/Redshift_Datasets/index.js b/frontend/src/modules/Redshift_Datasets/index.js new file mode 100644 index 000000000..a24217572 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/index.js @@ -0,0 +1,5 @@ +export const Redshift_DatasetsModule = { + moduleDefinition: true, + name: 'redshift_datasets', + isEnvironmentModule: false +}; diff --git a/frontend/src/modules/Redshift_Datasets/services/addRedshiftDatasetTables.js b/frontend/src/modules/Redshift_Datasets/services/addRedshiftDatasetTables.js new file mode 100644 index 000000000..ec001085f --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/addRedshiftDatasetTables.js @@ -0,0 +1,16 @@ +import { gql } from 'apollo-boost'; + +export const addRedshiftDatasetTables = ({ datasetUri, tables }) => ({ + variables: { + datasetUri, + tables + }, + mutation: gql` + mutation addRedshiftDatasetTables( + $datasetUri: String! + $tables: [String]! + ) { + addRedshiftDatasetTables(datasetUri: $datasetUri, tables: $tables) + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/services/deleteRedshiftDataset.js b/frontend/src/modules/Redshift_Datasets/services/deleteRedshiftDataset.js new file mode 100644 index 000000000..8d5abd517 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/deleteRedshiftDataset.js @@ -0,0 +1,12 @@ +import { gql } from 'apollo-boost'; + +export const deleteRedshiftDataset = (datasetUri) => ({ + variables: { + datasetUri + }, + mutation: gql` + mutation deleteRedshiftDataset($datasetUri: String!) { + deleteRedshiftDataset(datasetUri: $datasetUri) + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/services/deleteRedshiftDatasetTable.js b/frontend/src/modules/Redshift_Datasets/services/deleteRedshiftDatasetTable.js new file mode 100644 index 000000000..f5658fb2a --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/deleteRedshiftDatasetTable.js @@ -0,0 +1,12 @@ +import { gql } from 'apollo-boost'; + +export const deleteRedshiftDatasetTable = ({ rsTableUri }) => ({ + variables: { + rsTableUri + }, + mutation: gql` + mutation deleteRedshiftDatasetTable($rsTableUri: String!) { + deleteRedshiftDatasetTable(rsTableUri: $rsTableUri) + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/services/getRedshiftDataset.js b/frontend/src/modules/Redshift_Datasets/services/getRedshiftDataset.js new file mode 100644 index 000000000..64fc2ae3a --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/getRedshiftDataset.js @@ -0,0 +1,66 @@ +import { gql } from 'apollo-boost'; + +export const getRedshiftDataset = (datasetUri) => ({ + variables: { + datasetUri + }, + query: gql` + query getRedshiftDataset($datasetUri: String!) { + getRedshiftDataset(datasetUri: $datasetUri) { + datasetUri + owner + description + label + name + region + created + imported + userRoleForDataset + SamlAdminGroupName + AwsAccountId + tags + stewards + topics + confidentiality + autoApprovalEnabled + organization { + organizationUri + label + } + terms { + count + nodes { + __typename + ... on Term { + nodeUri + path + label + } + } + } + environment { + environmentUri + label + region + organization { + organizationUri + label + } + } + upvotes + connection { + connectionUri + label + redshiftType + clusterId + nameSpaceId + workgroup + redshiftUser + secretArn + database + } + schema + } + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/services/getRedshiftDatasetTable.js b/frontend/src/modules/Redshift_Datasets/services/getRedshiftDatasetTable.js new file mode 100644 index 000000000..0a0fbe5d9 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/getRedshiftDatasetTable.js @@ -0,0 +1,45 @@ +import { gql } from 'apollo-boost'; + +export const getRedshiftDatasetTable = ({ rsTableUri }) => ({ + variables: { + rsTableUri + }, + query: gql` + query getRedshiftDatasetTable($rsTableUri: String!) { + getRedshiftDatasetTable(rsTableUri: $rsTableUri) { + rsTableUri + name + label + created + description + tags + terms { + count + nodes { + __typename + ... on Term { + nodeUri + path + label + } + } + } + dataset { + owner + SamlAdminGroupName + datasetUri + name + label + userRoleForDataset + organization { + label + } + environment { + label + } + region + } + } + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/services/getRedshiftDatasetTableColumns.js b/frontend/src/modules/Redshift_Datasets/services/getRedshiftDatasetTableColumns.js new file mode 100644 index 000000000..7a3f0f8f6 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/getRedshiftDatasetTableColumns.js @@ -0,0 +1,30 @@ +import { gql } from 'apollo-boost'; + +export const getRedshiftDatasetTableColumns = ({ rsTableUri, filter }) => ({ + variables: { + rsTableUri, + filter + }, + query: gql` + query getRedshiftDatasetTableColumns( + $rsTableUri: String! + $filter: RedshiftDatasetTableFilter + ) { + getRedshiftDatasetTableColumns(rsTableUri: $rsTableUri, filter: $filter) { + count + page + pages + hasNext + hasPrevious + nodes { + columnDefault + label + length + name + nullable + typeName + } + } + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/services/importRedshiftDataset.js b/frontend/src/modules/Redshift_Datasets/services/importRedshiftDataset.js new file mode 100644 index 000000000..4f476fd19 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/importRedshiftDataset.js @@ -0,0 +1,16 @@ +import { gql } from 'apollo-boost'; + +export const importRedshiftDataset = (input) => ({ + variables: { + input + }, + mutation: gql` + mutation importRedshiftDataset($input: ImportRedshiftDatasetInput) { + importRedshiftDataset(input: $input) { + datasetUri + label + userRoleForDataset + } + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/services/index.js b/frontend/src/modules/Redshift_Datasets/services/index.js new file mode 100644 index 000000000..e21f732b0 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/index.js @@ -0,0 +1,13 @@ +export * from './addRedshiftDatasetTables'; +export * from './deleteRedshiftDataset'; +export * from './deleteRedshiftDatasetTable'; +export * from './getRedshiftDataset'; +export * from './getRedshiftDatasetTable'; +export * from './getRedshiftDatasetTableColumns'; +export * from './importRedshiftDataset'; +export * from './listRedshiftDatasetTables'; +export * from './listRedshiftConnectionSchemas'; +export * from './listRedshiftSchemaDatasetTables'; +export * from './listRedshiftSchemaTables'; +export * from './updateRedshiftDataset'; +export * from './updateRedshiftDatasetTable'; diff --git a/frontend/src/modules/Redshift_Datasets/services/listRedshiftConnectionSchemas.js b/frontend/src/modules/Redshift_Datasets/services/listRedshiftConnectionSchemas.js new file mode 100644 index 000000000..922bd66b6 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/listRedshiftConnectionSchemas.js @@ -0,0 +1,12 @@ +import { gql } from 'apollo-boost'; + +export const listRedshiftConnectionSchemas = ({ connectionUri }) => ({ + variables: { + connectionUri + }, + query: gql` + query listRedshiftConnectionSchemas($connectionUri: String!) { + listRedshiftConnectionSchemas(connectionUri: $connectionUri) + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/services/listRedshiftDatasetTables.js b/frontend/src/modules/Redshift_Datasets/services/listRedshiftDatasetTables.js new file mode 100644 index 000000000..5e85826f5 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/listRedshiftDatasetTables.js @@ -0,0 +1,30 @@ +import { gql } from 'apollo-boost'; + +export const listRedshiftDatasetTables = ({ datasetUri, filter }) => ({ + variables: { + datasetUri, + filter + }, + query: gql` + query listRedshiftDatasetTables( + $datasetUri: String! + $filter: RedshiftDatasetTableFilter + ) { + listRedshiftDatasetTables(datasetUri: $datasetUri, filter: $filter) { + count + page + pages + hasNext + hasPrevious + nodes { + rsTableUri + datasetUri + name + label + created + description + } + } + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/services/listRedshiftSchemaDatasetTables.js b/frontend/src/modules/Redshift_Datasets/services/listRedshiftSchemaDatasetTables.js new file mode 100644 index 000000000..7b063a08c --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/listRedshiftSchemaDatasetTables.js @@ -0,0 +1,16 @@ +import { gql } from 'apollo-boost'; + +export const listRedshiftSchemaDatasetTables = ({ datasetUri }) => ({ + variables: { + datasetUri + }, + query: gql` + query listRedshiftSchemaDatasetTables($datasetUri: String!) { + listRedshiftSchemaDatasetTables(datasetUri: $datasetUri) { + name + type + alreadyAdded + } + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/services/listRedshiftSchemaTables.js b/frontend/src/modules/Redshift_Datasets/services/listRedshiftSchemaTables.js new file mode 100644 index 000000000..55c2b827d --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/listRedshiftSchemaTables.js @@ -0,0 +1,16 @@ +import { gql } from 'apollo-boost'; + +export const listRedshiftSchemaTables = ({ connectionUri, schema }) => ({ + variables: { + connectionUri, + schema + }, + query: gql` + query listRedshiftSchemaTables($connectionUri: String!, $schema: String!) { + listRedshiftSchemaTables(connectionUri: $connectionUri, schema: $schema) { + name + type + } + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/services/updateRedshiftDataset.js b/frontend/src/modules/Redshift_Datasets/services/updateRedshiftDataset.js new file mode 100644 index 000000000..926b8e0e4 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/updateRedshiftDataset.js @@ -0,0 +1,20 @@ +import { gql } from 'apollo-boost'; + +export const updateRedshiftDataset = ({ datasetUri, input }) => ({ + variables: { + datasetUri, + input + }, + mutation: gql` + mutation updateRedshiftDataset( + $datasetUri: String! + $input: ModifyRedshiftDatasetInput + ) { + updateRedshiftDataset(datasetUri: $datasetUri, input: $input) { + datasetUri + label + userRoleForDataset + } + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/services/updateRedshiftDatasetTable.js b/frontend/src/modules/Redshift_Datasets/services/updateRedshiftDatasetTable.js new file mode 100644 index 000000000..7e5fa528b --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/services/updateRedshiftDatasetTable.js @@ -0,0 +1,19 @@ +import { gql } from 'apollo-boost'; + +export const updateRedshiftDatasetTable = ({ rsTableUri, input }) => ({ + variables: { + rsTableUri, + input + }, + mutation: gql` + mutation updateRedshiftDatasetTable( + $rsTableUri: String! + $input: ModifyRedshiftDatasetInput + ) { + updateRedshiftDatasetTable(rsTableUri: $rsTableUri, input: $input) { + rsTableUri + label + } + } + ` +}); diff --git a/frontend/src/modules/Redshift_Datasets/views/RSDatasetEditForm.js b/frontend/src/modules/Redshift_Datasets/views/RSDatasetEditForm.js new file mode 100644 index 000000000..e4ad5dad1 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/views/RSDatasetEditForm.js @@ -0,0 +1,606 @@ +import { LoadingButton } from '@mui/lab'; +import { + Autocomplete, + Box, + Breadcrumbs, + Button, + Card, + CardContent, + CardHeader, + Chip, + CircularProgress, + Container, + FormHelperText, + Grid, + Link, + MenuItem, + TextField, + Typography +} from '@mui/material'; +import { Formik } from 'formik'; +import { useSnackbar } from 'notistack'; +import React, { useCallback, useEffect, useState } from 'react'; +import { Helmet } from 'react-helmet-async'; +import { Link as RouterLink, useNavigate, useParams } from 'react-router-dom'; +import * as Yup from 'yup'; +import { + ArrowLeftIcon, + ChevronRightIcon, + ChipInput, + Defaults, + useSettings +} from 'design'; +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { listEnvironmentGroups, searchGlossary, useClient } from 'services'; +import { getRedshiftDataset, updateRedshiftDataset } from '../services'; +import { ConfidentialityList, Topics } from '../../constants'; +import config from '../../../generated/config.json'; +import { isFeatureEnabled } from 'utils'; + +const RSDatasetEditForm = (props) => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const params = useParams(); + const { enqueueSnackbar } = useSnackbar(); + const client = useClient(); + const { settings } = useSettings(); + const [loading, setLoading] = useState(true); + const [dataset, setDataset] = useState(null); + const [groupOptions, setGroupOptions] = useState([]); + const [selectableTerms, setSelectableTerms] = useState([]); + const [tableTerms, setTableTerms] = useState([]); + const [confidentialityOptions] = useState( + config.modules.datasets_base.features.confidentiality_dropdown === true && + config.modules.datasets_base.features.custom_confidentiality_mapping + ? Object.keys( + config.modules.datasets_base.features.custom_confidentiality_mapping + ) + : ConfidentialityList + ); + + const topicsData = Topics.map((t) => ({ label: t, value: t })); + + const fetchGroups = useCallback( + async (environmentUri) => { + try { + const response = await client.query( + listEnvironmentGroups({ + filter: Defaults.selectListFilter, + environmentUri + }) + ); + if (!response.errors) { + setGroupOptions( + response.data.listEnvironmentGroups.nodes.map((g) => ({ + value: g.groupUri, + label: g.groupUri + })) + ); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (e) { + dispatch({ type: SET_ERROR, error: e.message }); + } + }, + [client, dispatch] + ); + + const fetchItem = useCallback(async () => { + setLoading(true); + const response = await client.query(getRedshiftDataset(params.uri)); + if (!response.errors && response.data.getRedshiftDataset !== null) { + setDataset(response.data.getRedshiftDataset); + fetchGroups( + response.data.getRedshiftDataset.environment.environmentUri + ).catch((e) => dispatch({ type: SET_ERROR, error: e.message })); + let fetchedTerms = []; + if ( + response.data.getRedshiftDataset.terms && + response.data.getRedshiftDataset.terms.nodes.length > 0 + ) { + fetchedTerms = response.data.getRedshiftDataset.terms.nodes.map( + (node) => ({ + label: node.label, + value: node.nodeUri, + nodeUri: node.nodeUri, + disabled: node.__typename !== 'Term' /*eslint-disable-line*/, + nodePath: node.path, + nodeType: node.__typename /*eslint-disable-line*/ + }) + ); + } + setTableTerms(fetchedTerms); + client.query(searchGlossary(Defaults.selectListFilter)).then((result) => { + if ( + result.data.searchGlossary && + result.data.searchGlossary.nodes.length > 0 + ) { + const selectables = result.data.searchGlossary.nodes.map((node) => ({ + label: node.label, + value: node.nodeUri, + nodeUri: node.nodeUri, + disabled: node.__typename !== 'Term' /* eslint-disable-line*/, + nodePath: node.path, + nodeType: node.__typename /* eslint-disable-line*/ + })); + setSelectableTerms(selectables); + } + }); + } else { + const error = response.errors + ? response.errors[0].message + : 'Dataset not found'; + dispatch({ type: SET_ERROR, error }); + } + setLoading(false); + }, [client, dispatch, params.uri, fetchGroups]); + + useEffect(() => { + if (client) { + fetchItem().catch((e) => dispatch({ type: SET_ERROR, error: e.message })); + } + }, [client, dispatch, fetchItem]); + + async function submit(values, setStatus, setSubmitting, setErrors) { + try { + const response = await client.mutate( + updateRedshiftDataset({ + datasetUri: dataset.datasetUri, + input: { + label: values.label, + description: values.description, + tags: values.tags, + stewards: values.stewards, + topics: values.topics ? values.topics.map((t) => t.value) : [], + terms: values.terms.nodes + ? values.terms.nodes.map((t) => t.nodeUri) + : values.terms.map((t) => t.nodeUri), + confidentiality: values.confidentiality, + autoApprovalEnabled: values.autoApprovalEnabled + } + }) + ); + if (!response.errors) { + setStatus({ success: true }); + setSubmitting(false); + enqueueSnackbar('Dataset updated', { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + }); + navigate( + `/console/redshift-datasets/${response.data.updateRedshiftDataset.datasetUri}` + ); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (err) { + console.error(err); + setStatus({ success: false }); + setErrors({ submit: err.message }); + setSubmitting(false); + dispatch({ type: SET_ERROR, error: err.message }); + } + } + + if (loading || !(dataset && dataset.environment)) { + return ; + } + + return ( + <> + + Dataset: Dataset Update | data.all + + + + + + + Edit dataset {dataset.label} + + } + sx={{ mt: 1 }} + > + + Contribute + + + Datasets + + + {dataset.label} + + + Edit + + + + + + + + + + + ({ label: s, value: s })), + tags: dataset.tags, + terms: dataset.terms || [], + stewards: dataset.stewards, + confidentiality: dataset.confidentiality, + autoApprovalEnabled: dataset.autoApprovalEnabled + }} + validationSchema={Yup.object().shape({ + label: Yup.string() + .max(255) + .required('*Dataset name is required'), + description: Yup.string().max(5000), + topics: isFeatureEnabled('datasets_base', 'topics_dropdown') + ? Yup.array().min(1).required('*Topics are required') + : Yup.array(), + tags: Yup.array().min(1).required('*Tags are required'), + confidentiality: isFeatureEnabled( + 'datasets_base', + 'confidentiality_dropdown' + ) + ? Yup.string() + .max(255) + .required('*Confidentiality is required') + : Yup.string(), + autoApprovalEnabled: Yup.boolean().required( + '*AutoApproval property is required' + ) + })} + onSubmit={async ( + values, + { setErrors, setStatus, setSubmitting } + ) => { + await submit(values, setStatus, setSubmitting, setErrors); + }} + > + {({ + errors, + handleBlur, + handleChange, + handleSubmit, + isSubmitting, + setFieldValue, + touched, + values + }) => ( +
+ + + + + + + + + + {touched.description && errors.description && ( + + + {errors.description} + + + )} + + + + + {isFeatureEnabled( + 'datasets_base', + 'confidentiality_dropdown' + ) && ( + + + {confidentialityOptions.map((c) => ( + + {c} + + ))} + + + )} + {isFeatureEnabled( + 'datasets_base', + 'topics_dropdown' + ) && ( + + + option.value === value.value + } + getOptionLabel={(opt) => opt.label} + onChange={(event, value) => { + setFieldValue('topics', value); + }} + renderTags={(tagValue, getTagProps) => + tagValue.map((option, index) => ( + + )) + } + renderInput={(p) => ( + + )} + /> + + )} + + {dataset && ( + ({ + label: node.label, + nodeUri: node.nodeUri + }))} + getOptionLabel={(opt) => opt.label} + getOptionDisabled={(opt) => opt.disabled} + getOptionSelected={(option, value) => + option.nodeUri === value.nodeUri + } + onChange={(event, value) => { + setFieldValue('terms', value); + }} + renderTags={(tagValue, getTagProps) => + tagValue.map((option, index) => ( + + )) + } + renderInput={(p) => ( + + )} + /> + )} + + + + { + setFieldValue('tags', [...chip]); + }} + /> + + + + {config.modules.datasets_base.features + .auto_approval_for_confidentiality_level[ + values.confidentiality + ] === true && ( + + + Enabled + + + Disabled + + + )} + + + + + + + + + + + + + + + + + + + + + + + option.value)} + onChange={(event, value) => { + setFieldValue('stewards', value); + }} + renderInput={(renderParams) => ( + + )} + /> + + + + + Save + + + + +
+ )} +
+
+
+
+ + ); +}; + +export default RSDatasetEditForm; diff --git a/frontend/src/modules/Redshift_Datasets/views/RSDatasetImportForm.js b/frontend/src/modules/Redshift_Datasets/views/RSDatasetImportForm.js new file mode 100644 index 000000000..f1bfc0f04 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/views/RSDatasetImportForm.js @@ -0,0 +1,830 @@ +import { LoadingButton } from '@mui/lab'; +import { + Autocomplete, + Box, + Breadcrumbs, + Button, + Card, + CardContent, + CardHeader, + Chip, + CircularProgress, + Container, + FormHelperText, + Grid, + Link, + MenuItem, + TextField, + Typography +} from '@mui/material'; +import LinearProgress from '@mui/material/LinearProgress'; +import { Formik } from 'formik'; +import { useSnackbar } from 'notistack'; +import React, { useCallback, useEffect, useState } from 'react'; +import { Helmet } from 'react-helmet-async'; +import { Link as RouterLink, useNavigate } from 'react-router-dom'; +import * as Yup from 'yup'; +import { + ArrowLeftIcon, + ChevronRightIcon, + ChipInput, + Defaults, + Scrollbar, + useSettings +} from 'design'; +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { + listEnvironmentGroups, + listValidEnvironments, + listEnvironmentRedshiftConnections, + useClient +} from 'services'; +import { + importRedshiftDataset, + listRedshiftConnectionSchemas, + listRedshiftSchemaTables +} from '../services'; +import { Topics, ConfidentialityList } from '../../constants'; +import config from '../../../generated/config.json'; +import { isFeatureEnabled } from 'utils'; +import { DataGrid } from '@mui/x-data-grid'; + +const RSDatasetImportForm = (props) => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const { enqueueSnackbar } = useSnackbar(); + const client = useClient(); + const { settings } = useSettings(); + const [loading, setLoading] = useState(true); + const [loadingSchemas, setLoadingSchemas] = useState(false); + const [loadingTables, setLoadingTables] = useState(false); + const [groupOptions, setGroupOptions] = useState([]); + const [environmentOptions, setEnvironmentOptions] = useState([]); + const [connectionOptions, setConnectionOptions] = useState([]); + const [schemaOptions, setSchemaOptions] = useState([]); + const [tableOptions, setTableOptions] = useState(null); + const [filter, setFilter] = useState(Defaults.filter); + const [confidentialityOptions] = useState( + config.modules.datasets_base.features.confidentiality_dropdown === true && + config.modules.datasets_base.features.custom_confidentiality_mapping + ? Object.keys( + config.modules.datasets_base.features.custom_confidentiality_mapping + ) + : ConfidentialityList + ); + + const topicsData = Topics.map((t) => ({ label: t, value: t })); + + const fetchEnvironments = useCallback(async () => { + setLoading(true); + const response = await client.query( + listValidEnvironments({ + filter: Defaults.selectListFilter + }) + ); + if (!response.errors) { + setEnvironmentOptions( + response.data.listValidEnvironments.nodes.map((e) => ({ + ...e, + value: e.environmentUri, + label: e.label + })) + ); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + setLoading(false); + }, [client, dispatch]); + const fetchGroups = async (environmentUri) => { + try { + const response = await client.query( + listEnvironmentGroups({ + filter: Defaults.selectListFilter, + environmentUri + }) + ); + if (!response.errors) { + setGroupOptions( + response.data.listEnvironmentGroups.nodes.map((g) => ({ + value: g.groupUri, + label: g.groupUri + })) + ); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (e) { + dispatch({ type: SET_ERROR, error: e.message }); + } + }; + + const fetchRedshiftConnections = async (environmentUri, groupUri) => { + try { + const response = await client.query( + listEnvironmentRedshiftConnections({ + filter: { + ...Defaults.selectListFilter, + environmentUri: environmentUri, + groupUri: groupUri + } + }) + ); + if (!response.errors) { + setConnectionOptions( + response.data.listEnvironmentRedshiftConnections.nodes.map((g) => ({ + value: g.connectionUri, + label: `${g.name} [DATABASE: ${g.database}]` + })) + ); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (e) { + dispatch({ type: SET_ERROR, error: e.message }); + } + }; + + const fetchSchemas = async (connectionUri) => { + setLoadingSchemas(true); + try { + const response = await client.query( + listRedshiftConnectionSchemas({ + connectionUri + }) + ); + if (!response.errors) { + setSchemaOptions(response.data.listRedshiftConnectionSchemas); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (e) { + dispatch({ type: SET_ERROR, error: e.message }); + } + setLoadingSchemas(false); + }; + + const fetchTables = async (connectionUri, schema) => { + setLoadingTables(true); + try { + const response = await client.query( + listRedshiftSchemaTables({ + connectionUri, + schema + }) + ); + if (!response.errors) { + setTableOptions(response.data.listRedshiftSchemaTables); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (e) { + dispatch({ type: SET_ERROR, error: e.message }); + } + setLoadingTables(false); + }; + + useEffect(() => { + if (client) { + fetchEnvironments().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + } + }, [client, dispatch, fetchEnvironments]); + + async function submit(values, setStatus, setSubmitting, setErrors) { + try { + const response = await client.mutate( + importRedshiftDataset({ + organizationUri: values.environment.organization.organizationUri, + environmentUri: values.environment.environmentUri, + owner: '', + label: values.label, + SamlAdminGroupName: values.SamlAdminGroupName, + tags: values.tags, + description: values.description, + topics: values.topics ? values.topics.map((t) => t.value) : [], + stewards: values.stewards, + confidentiality: values.confidentiality, + autoApprovalEnabled: values.autoApprovalEnabled, + connectionUri: values.connection.value, + schema: values.schema, + tables: values.tables + }) + ); + if (!response.errors) { + setStatus({ success: true }); + setSubmitting(false); + enqueueSnackbar('Dataset imported successfully', { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + }); + navigate( + `/console/redshift-datasets/${response.data.importRedshiftDataset.datasetUri}` + ); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (err) { + console.error(err); + setStatus({ success: false }); + setErrors({ submit: err.message }); + setSubmitting(false); + dispatch({ type: SET_ERROR, error: err.message }); + } + } + + if (loading) { + return ; + } + + return ( + <> + + Dataset: Redshift Dataset Import | data.all + + + + + + + Import a new Redshift dataset + + } + sx={{ mt: 1 }} + > + + Contribute + + + Datasets + + + Import + + + + + + + + + + + { + await submit(values, setStatus, setSubmitting, setErrors); + }} + > + {({ + errors, + handleBlur, + handleChange, + handleSubmit, + isSubmitting, + setFieldValue, + touched, + values + }) => ( +
+ + + + + + + + + + {touched.description && errors.description && ( + + + {errors.description} + + + )} + + + + + {isFeatureEnabled( + 'datasets_base', + 'confidentiality_dropdown' + ) && ( + + + {confidentialityOptions.map((c) => ( + + {c} + + ))} + + + )} + {isFeatureEnabled( + 'datasets_base', + 'topics_dropdown' + ) && ( + + opt.label} + onChange={(event, value) => { + setFieldValue('topics', value); + }} + renderTags={(tagValue, getTagProps) => + tagValue.map((option, index) => ( + + )) + } + renderInput={(p) => ( + + )} + /> + + )} + + + { + setFieldValue('tags', [...chip]); + }} + /> + + + + {config.modules.datasets_base.features + .auto_approval_for_confidentiality_level[ + values.confidentiality + ] === true && ( + + + Enabled + + + Disabled + + + )} + + + + + + + + option)} + onChange={(event, value) => { + setFieldValue('SamlAdminGroupName', ''); + setFieldValue('stewards', ''); + if (value && value.environmentUri) { + setFieldValue('environment', value); + fetchGroups(value.environmentUri).catch((e) => + dispatch({ + type: SET_ERROR, + error: e.message + }) + ); + } else { + setFieldValue('environment', ''); + setFieldValue('SamlAdminGroup', ''); + setFieldValue('connection', ''); + setFieldValue('schema', ''); + setFieldValue('tables', []); + setGroupOptions([]); + setConnectionOptions([]); + setSchemaOptions([]); + setTableOptions(null); + } + }} + renderInput={(params) => ( + + )} + /> + + + + + + option)} + onChange={(event, value) => { + if (value && value.value) { + setFieldValue( + 'SamlAdminGroupName', + value.value + ); + fetchRedshiftConnections( + values.environment.environmentUri, + value.value + ).catch((e) => + dispatch({ + type: SET_ERROR, + error: e.message + }) + ); + } else { + setFieldValue('SamlAdminGroupName', ''); + setFieldValue('connection', ''); + setFieldValue('schema', ''); + setFieldValue('tables', []); + setConnectionOptions([]); + setSchemaOptions([]); + setTableOptions(null); + } + }} + inputValue={values.SamlAdminGroupName} + renderInput={(params) => ( + + )} + /> + + + option)} + onChange={(event, value) => { + if (value && value.value) { + setFieldValue('stewards', value.value); + } else { + setFieldValue('stewards', ''); + } + }} + inputValue={values.stewards} + renderInput={(params) => ( + + )} + /> + + + + + + option)} + noOptionsText="No connections for the selected Team and Environment" + onChange={(event, value) => { + if (value && value) { + setFieldValue('connection', value); + fetchSchemas(value.value).catch((e) => + dispatch({ + type: SET_ERROR, + error: e.message + }) + ); + } else { + setFieldValue('connection', ''); + setSchemaOptions([]); + setTableOptions(null); + setFieldValue('schema', ''); + setFieldValue('tables', []); + } + }} + inputValue={values.connection.label} + renderInput={(params) => ( + + )} + /> + + + option)} + noOptionsText="No schemas for the selected Connection" + onChange={(event, value) => { + if (value) { + setFieldValue('schema', value); + fetchTables( + values.connection.value, + value + ).catch((e) => + dispatch({ + type: SET_ERROR, + error: e.message + }) + ); + } else { + setTableOptions(null); + setFieldValue('schema', ''); + setFieldValue('tables', []); + } + }} + inputValue={values.schema} + renderInput={(params) => ( + + )} + /> + + {loadingTables && ( + + + Loading database tables + + + + )} + {tableOptions && ( + + + + node.name} + rows={tableOptions} + columns={[ + { field: 'id', hide: true }, + { + field: 'name', + headerName: 'Redshift tables', + flex: 0.5, + editable: false + } + ]} + pageSize={filter.pageSize} + rowsPerPageOptions={[filter.pageSize]} + loading={loading} + onPageSizeChange={(pageSize) => { + setFilter({ + ...filter, + pageSize: pageSize + }); + }} + getRowHeight={() => 'auto'} + disableSelectionOnClick + onSelectionModelChange={( + newSelectionModel + ) => { + setFieldValue('tables', newSelectionModel); + }} + components={{ + LoadingOverlay: LinearProgress + }} + sx={{ wordWrap: 'break-word' }} + /> + + + + )} + + + + Import Dataset + + + + +
+ )} +
+
+
+
+ + ); +}; + +export default RSDatasetImportForm; diff --git a/frontend/src/modules/Redshift_Datasets/views/RSDatasetView.js b/frontend/src/modules/Redshift_Datasets/views/RSDatasetView.js new file mode 100644 index 000000000..4b1d50cd9 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/views/RSDatasetView.js @@ -0,0 +1,327 @@ +import { + ForumOutlined, + Info, + ViewArrayOutlined, + Warning +} from '@mui/icons-material'; +import { + Box, + Breadcrumbs, + Button, + Card, + CardContent, + CircularProgress, + Container, + Divider, + Grid, + Link, + Tab, + Tabs, + Typography +} from '@mui/material'; +import { useSnackbar } from 'notistack'; +import React, { useCallback, useEffect, useState } from 'react'; +import { Helmet } from 'react-helmet-async'; +import { FaTrash } from 'react-icons/fa'; +import { useNavigate } from 'react-router'; +import { Link as RouterLink, useParams } from 'react-router-dom'; +import { + ChevronRightIcon, + DeleteObjectWithFrictionModal, + PencilAltIcon, + UpVoteButton, + useSettings +} from 'design'; +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { countUpVotes, getVote, upVote, useClient } from 'services'; +import { deleteRedshiftDataset, getRedshiftDataset } from '../services'; +import { FeedComments } from 'modules/Shared'; +import { RedshiftDatasetTables, RedshiftDatasetOverview } from '../components'; + +const RSDatasetView = () => { + const dispatch = useDispatch(); + const { settings } = useSettings(); + const { enqueueSnackbar } = useSnackbar(); + const params = useParams(); + const client = useClient(); + const navigate = useNavigate(); + const [currentTab, setCurrentTab] = useState('overview'); + const [loading, setLoading] = useState(true); + const [dataset, setDataset] = useState(null); + const [isDeleteObjectModalOpen, setIsDeleteObjectModalOpen] = useState(false); + const [isAdmin, setIsAdmin] = useState(false); + const [isUpVoted, setIsUpVoted] = useState(false); + const [upVotes, setUpvotes] = useState(null); + const [openFeed, setOpenFeed] = useState(false); + const getTabs = () => { + const tabs = [ + { label: 'Overview', value: 'overview', icon: }, + { + label: 'Data', + value: 'data', + icon: + } + ]; + return tabs; + }; + + const handleDeleteObjectModalOpen = () => { + setIsDeleteObjectModalOpen(true); + }; + + const handleDeleteObjectModalClose = () => { + setIsDeleteObjectModalOpen(false); + }; + + const getUserDatasetVote = useCallback( + async (datasetUri) => { + const response = await client.query( + getVote(datasetUri, 'redshiftdataset') + ); + if (!response.errors && response.data.getVote !== null) { + setIsUpVoted(response.data.getVote.upvote); + } + }, + [client] + ); + + const reloadVotes = async () => { + const response = await client.query( + countUpVotes(params.uri, 'redshiftdataset') + ); + if (!response.errors && response.data.countUpVotes !== null) { + setUpvotes(response.data.countUpVotes); + } else { + setUpvotes(0); + } + }; + + const upVoteDataset = async (datasetUri) => { + const response = await client.mutate( + upVote({ + targetUri: datasetUri, + targetType: 'redshiftdataset', + upvote: !isUpVoted + }) + ); + if (!response.errors && response.data.upVote !== null) { + setIsUpVoted(response.data.upVote.upvote); + } + reloadVotes().catch((e) => dispatch({ type: SET_ERROR, error: e.message })); + }; + + const fetchItem = useCallback(async () => { + setLoading(true); + const response = await client.query(getRedshiftDataset(params.uri)); + if (!response.errors && response.data.getRedshiftDataset !== null) { + setDataset(response.data.getRedshiftDataset); + setIsAdmin( + ['BusinessOwner', 'Admin', 'DataSteward', 'Creator'].indexOf( + response.data.getRedshiftDataset.userRoleForDataset + ) !== -1 + ); + setUpvotes(response.data.getRedshiftDataset.upvotes); + } else { + const error = response.errors + ? response.errors[0].message + : 'Dataset not found'; + dispatch({ type: SET_ERROR, error }); + } + setLoading(false); + }, [client, dispatch, params.uri]); + + useEffect(() => { + if (client) { + getUserDatasetVote(params.uri).catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + fetchItem().catch((e) => dispatch({ type: SET_ERROR, error: e.message })); + } + }, [client, fetchItem, getUserDatasetVote, dispatch, params.uri]); + + const handleTabsChange = (event, value) => { + setCurrentTab(value); + }; + + const removeDataset = async (deleteFromAWS = false) => { + const response = await client.mutate( + deleteRedshiftDataset(dataset.datasetUri, deleteFromAWS) + ); + if (!response.errors) { + handleDeleteObjectModalClose(); + enqueueSnackbar('Dataset deleted', { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + }); + navigate('/console/datasets'); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + }; + + if (loading) { + return ; + } + if (!dataset) { + return null; + } + + return ( + <> + + Datasets: Dataset Details | data.all + + + + + + + Redshift Dataset {dataset.label} + + } + sx={{ mt: 1 }} + > + + Contribute + + + Datasets + + + {dataset.label} + + + + + + {isAdmin && ( + + upVoteDataset(dataset.datasetUri)} + upVotes={upVotes} + /> + + + + + )} + + + + + + {getTabs().map((tab) => ( + + ))} + + + + + {currentTab === 'data' && ( + + )} + {currentTab === 'overview' && ( + + )} + + + + {isAdmin && ( + + + + Redshift Dataset will be deleted from data.all + catalog, but its tables and schema will still be available in + Amazon Redshift. + + + + } + isAWSResource={false} + /> + )} + {openFeed && ( + setOpenFeed(false)} + /> + )} + + ); +}; + +export default RSDatasetView; diff --git a/frontend/src/modules/Redshift_Datasets/views/RSTableEditForm.js b/frontend/src/modules/Redshift_Datasets/views/RSTableEditForm.js new file mode 100644 index 000000000..6a271af7a --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/views/RSTableEditForm.js @@ -0,0 +1,392 @@ +import { LoadingButton } from '@mui/lab'; +import { + Autocomplete, + Box, + Breadcrumbs, + Button, + Card, + CardContent, + CardHeader, + Chip, + Container, + FormHelperText, + Grid, + Link, + TextField, + Typography +} from '@mui/material'; +import CircularProgress from '@mui/material/CircularProgress'; +import { Formik } from 'formik'; +import { useSnackbar } from 'notistack'; +import * as PropTypes from 'prop-types'; +import { useEffect, useState } from 'react'; +import { Helmet } from 'react-helmet-async'; +import { Link as RouterLink, useNavigate, useParams } from 'react-router-dom'; +import { + ArrowLeftIcon, + ChevronRightIcon, + ChipInput, + Defaults, + useSettings +} from 'design'; +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { searchGlossary, useClient } from 'services'; +import { + getRedshiftDatasetTable, + updateRedshiftDatasetTable +} from '../services'; + +function TableEditHeader(props) { + const { table } = props; + return ( + + + + {`Update Table: ${table.label}`} + + } + sx={{ mt: 1 }} + > + + Discover + + + Datasets + + + {table.dataset.name} + + + + {table.name} + + + + Edit + + + + + + + + + + ); +} + +TableEditHeader.propTypes = { table: PropTypes.object.isRequired }; + +const RSTableEditForm = () => { + const dispatch = useDispatch(); + const { settings } = useSettings(); + const params = useParams(); + const client = useClient(); + const navigate = useNavigate(); + const { enqueueSnackbar } = useSnackbar(); + const [table, setTable] = useState({}); + const [loading, setLoading] = useState(true); + const [selectableTerms, setSelectableTerms] = useState([]); + const [tableTerms, setTableTerms] = useState([]); + + async function submit(values, setStatus, setSubmitting, setErrors) { + try { + await client.mutate( + updateRedshiftDatasetTable({ + rsTableUri: table.rsTableUri, + input: { + description: values.description, + terms: values.terms.nodes + ? values.terms.nodes.map((t) => t.nodeUri) + : values.terms.map((t) => t.nodeUri), + tags: values.tags + } + }) + ); + setStatus({ success: true }); + setSubmitting(false); + enqueueSnackbar('Table updated', { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + }); + navigate(`/console/redshift-datasets/table/${table.rsTableUri}`); + } catch (err) { + console.error(err); + setStatus({ success: false }); + setErrors({ submit: err.message }); + setSubmitting(false); + dispatch({ type: SET_ERROR, error: err.message }); + } + } + + useEffect(() => { + const fetchItem = async () => { + setLoading(true); + let response = await client.query( + getRedshiftDatasetTable({ rsTableUri: params.uri }) + ); + let fetchedTerms = []; + if (!response.errors && response.data.getRedshiftDatasetTable !== null) { + setTable(response.data.getRedshiftDatasetTable); + if ( + response.data.getRedshiftDatasetTable.terms && + response.data.getRedshiftDatasetTable.terms.nodes.length > 0 + ) { + fetchedTerms = response.data.getRedshiftDatasetTable.terms.nodes.map( + (node) => ({ + label: node.label, + value: node.nodeUri, + nodeUri: node.nodeUri, + disabled: node.__typename !== 'Term' /*eslint-disable-line*/, + nodePath: node.path, + nodeType: node.__typename /*eslint-disable-line*/ + }) + ); + } + setTableTerms(fetchedTerms); + response = client.query(searchGlossary(Defaults.selectListFilter)); + response.then((result) => { + if ( + result.data.searchGlossary && + result.data.searchGlossary.nodes.length > 0 + ) { + const selectables = result.data.searchGlossary.nodes.map( + (node) => ({ + label: node.label, + value: node.nodeUri, + nodeUri: node.nodeUri, + disabled: node.__typename !== 'Term' /* eslint-disable-line*/, + nodePath: node.path, + nodeType: node.__typename /* eslint-disable-line*/ + }) + ); + setSelectableTerms(selectables); + } + }); + } else { + const error = response.errors + ? response.errors[0].message + : 'Dataset table not found'; + dispatch({ type: SET_ERROR, error }); + } + setLoading(false); + }; + if (client) { + fetchItem().catch((e) => dispatch({ type: SET_ERROR, error: e.message })); + } + }, [client, dispatch, params.uri]); + + if (loading) { + return ; + } + if (!table) { + return null; + } + + return ( + <> + + Tables: Table Update | data.all + + + + + + { + await submit(values, setStatus, setSubmitting, setErrors); + }} + > + {({ + errors, + handleBlur, + handleChange, + handleSubmit, + isSubmitting, + setFieldValue, + touched, + values + }) => ( +
+ + + + + + + + + + + {touched.description && errors.description && ( + + + {errors.description} + + + )} + + + + + + + + + + { + setFieldValue('tags', [...chip]); + }} + /> + + + {table && ( + ({ + label: node.label, + nodeUri: node.nodeUri + }))} + getOptionLabel={(opt) => opt.label} + getOptionDisabled={(opt) => opt.disabled} + getOptionSelected={(option, value) => + option.nodeUri === value.nodeUri + } + onChange={(event, value) => { + setFieldValue('terms', value); + }} + renderTags={(tagValue, getTagProps) => + tagValue.map((option, index) => ( + + )) + } + renderInput={(p) => ( + + )} + /> + )} + + + + {errors.submit && ( + + {errors.submit} + + )} + + + Update table + + + + +
+ )} +
+
+
+
+ + ); +}; + +export default RSTableEditForm; diff --git a/frontend/src/modules/Redshift_Datasets/views/RSTableView.js b/frontend/src/modules/Redshift_Datasets/views/RSTableView.js new file mode 100644 index 000000000..ecfea2806 --- /dev/null +++ b/frontend/src/modules/Redshift_Datasets/views/RSTableView.js @@ -0,0 +1,301 @@ +import { ForumOutlined, Warning } from '@mui/icons-material'; +import { + Box, + Breadcrumbs, + Button, + Card, + CardContent, + CircularProgress, + Container, + Divider, + Grid, + Link, + Tab, + Tabs, + Typography +} from '@mui/material'; +import * as PropTypes from 'prop-types'; +import React, { useCallback, useEffect, useState } from 'react'; +import { Helmet } from 'react-helmet-async'; +import { FaTrash } from 'react-icons/fa'; +import { useNavigate } from 'react-router'; +import { useSnackbar } from 'notistack'; +import { Link as RouterLink, useParams } from 'react-router-dom'; +import { + ChevronRightIcon, + DeleteObjectModal, + PencilAltIcon, + useSettings +} from 'design'; +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { useClient } from 'services'; +import { FeedComments } from 'modules/Shared'; +import { + getRedshiftDatasetTable, + deleteRedshiftDatasetTable +} from '../services'; +import { TableColumns, TableOverview } from '../components'; + +const tabs = [ + { label: 'Overview', value: 'overview' }, + { label: 'Columns', value: 'columns' } +]; + +function TablePageHeader(props) { + const { table, handleDeleteObjectModalOpen, isAdmin } = props; + const [openFeed, setOpenFeed] = useState(false); + return ( + + + + Table {table.name} + + } + sx={{ mt: 1 }} + > + + Discover + + + Datasets + + + {table?.dataset?.name} + + + {table.name} + + + + {isAdmin && ( + + + + + + + + )} + {openFeed && ( + setOpenFeed(false)} + /> + )} + + ); +} + +TablePageHeader.propTypes = { + table: PropTypes.object.isRequired, + handleDeleteObjectModalOpen: PropTypes.func.isRequired, + isAdmin: PropTypes.bool.isRequired +}; + +const RSTableView = () => { + const dispatch = useDispatch(); + const { enqueueSnackbar } = useSnackbar(); + const { settings } = useSettings(); + const params = useParams(); + const client = useClient(); + const navigate = useNavigate(); + const [table, setTable] = useState({}); + const [currentTab, setCurrentTab] = useState(tabs[0].value); + const [loading, setLoading] = useState(true); + const [isDeleteObjectModalOpen, setIsDeleteObjectModalOpen] = useState(false); + const [isAdmin, setIsAdmin] = useState(false); + + const handleDeleteObjectModalOpen = () => { + setIsDeleteObjectModalOpen(true); + }; + const handleDeleteObjectModalClose = () => { + setIsDeleteObjectModalOpen(false); + }; + + const deleteTable = async () => { + const response = await client.mutate( + deleteRedshiftDatasetTable({ + rsTableUri: table.rsTableUri + }) + ); + if (!response.errors) { + enqueueSnackbar('Table deleted', { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + }); + navigate(`/console/redshift-datasets/${table.datasetUri}`); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + }; + + const fetchItem = useCallback(async () => { + setLoading(true); + const response = await client.query( + getRedshiftDatasetTable({ rsTableUri: params.uri }) + ); + if (!response.errors && response.data.getRedshiftDatasetTable !== null) { + setTable(response.data.getRedshiftDatasetTable); + setIsAdmin( + ['Creator', 'Admin', 'Owner'].indexOf( + response.data.getRedshiftDatasetTable.dataset.userRoleForDataset + ) !== -1 + ); + } else { + setTable(null); + const error = response.errors + ? response.errors[0].message + : 'Redshift Dataset table not found'; + dispatch({ type: SET_ERROR, error }); + } + setLoading(false); + }, [client, dispatch, params.uri]); + + useEffect(() => { + if (client) { + fetchItem().catch((e) => dispatch({ type: SET_ERROR, error: e.message })); + } + }, [client, fetchItem, dispatch]); + + const handleTabsChange = (event, value) => { + setCurrentTab(value); + }; + + if (loading) { + return ; + } + + if (!table) { + return null; + } + + return ( + <> + + Tables: Table Details | data.all + + + + + + + {tabs.map((tab) => ( + + ))} + + + + + {currentTab === 'overview' && ( + + )} + {currentTab === 'columns' && } + + + + {isAdmin && ( + + + + Redshift Table will be deleted from data.all + catalog, but will still be available in Amazon Redshift. + + + + } + /> + )} + + ); +}; + +export default RSTableView; diff --git a/frontend/src/modules/S3_Datasets/components/DatasetGovernance.js b/frontend/src/modules/S3_Datasets/components/DatasetGovernance.js deleted file mode 100644 index 4d8d9b3f2..000000000 --- a/frontend/src/modules/S3_Datasets/components/DatasetGovernance.js +++ /dev/null @@ -1,115 +0,0 @@ -import { - Box, - Card, - CardContent, - CardHeader, - Chip, - Divider, - Typography -} from '@mui/material'; -import PropTypes from 'prop-types'; -import { Label } from 'design'; -import { isFeatureEnabled } from 'utils'; - -export const DatasetGovernance = (props) => { - const { dataset } = props; - const terms = - dataset.terms.nodes.length > 0 - ? dataset.terms.nodes - : [{ label: '-', nodeUri: '-' }]; - const tags = dataset.tags.length > 0 ? dataset.tags : ['-']; - - return ( - - - - - - Owners - - - {dataset.SamlAdminGroupName} - - - - - Stewards - - - {dataset.stewards} - - - - - Auto-Approval - - - - - - {isFeatureEnabled('datasets_base', 'confidentiality_dropdown') && ( - - - Classification - - - - - - )} - {isFeatureEnabled('datasets_base', 'topics_dropdown') && ( - - - Topics - - - {dataset.topics && - dataset.topics.length > 0 && - dataset.topics.map((t) => ( - - ))} - - - )} - - - - Tags - - - {tags && - tags.map((t) => ( - - ))} - - - - - Glossary terms - - - {terms && - terms.map((term) => ( - - ))} - - - - ); -}; - -DatasetGovernance.propTypes = { - dataset: PropTypes.object.isRequired -}; diff --git a/frontend/src/modules/S3_Datasets/components/DatasetOverview.js b/frontend/src/modules/S3_Datasets/components/DatasetOverview.js index 83ce686ea..7d7d4ed62 100644 --- a/frontend/src/modules/S3_Datasets/components/DatasetOverview.js +++ b/frontend/src/modules/S3_Datasets/components/DatasetOverview.js @@ -2,7 +2,7 @@ import { Box, Grid } from '@mui/material'; import PropTypes from 'prop-types'; import { ObjectBrief, ObjectMetadata } from 'design'; import { DatasetConsoleAccess } from './DatasetConsoleAccess'; -import { DatasetGovernance } from './DatasetGovernance'; +import { DatasetGovernance } from 'modules/DatasetsBase/components/DatasetGovernance'; export const DatasetOverview = (props) => { const { dataset, isAdmin, ...other } = props; diff --git a/frontend/src/modules/S3_Datasets/components/index.js b/frontend/src/modules/S3_Datasets/components/index.js index 31b7ca3bc..7e899832c 100644 --- a/frontend/src/modules/S3_Datasets/components/index.js +++ b/frontend/src/modules/S3_Datasets/components/index.js @@ -2,7 +2,6 @@ export * from './DatasetAWSActions'; export * from './DatasetConsoleAccess'; export * from './DatasetData'; export * from './DatasetFolders'; -export * from './DatasetGovernance'; export * from './DatasetOverview'; export * from './DatasetStartCrawlerModal'; export * from './DatasetTables'; diff --git a/frontend/src/modules/Tables/index.js b/frontend/src/modules/Tables/index.js new file mode 100644 index 000000000..a49758dc3 --- /dev/null +++ b/frontend/src/modules/Tables/index.js @@ -0,0 +1,10 @@ +import { getModuleActiveStatus, ModuleNames } from 'utils'; + +export const S3TablesModule = { + moduleDefinition: true, + name: 's3_tables', + isEnvironmentModule: false, + resolve_dependency: () => { + return getModuleActiveStatus(ModuleNames.S3_DATASETS); + } +}; diff --git a/frontend/src/modules/index.js b/frontend/src/modules/index.js index 47718e776..3acdc8bc3 100644 --- a/frontend/src/modules/index.js +++ b/frontend/src/modules/index.js @@ -7,6 +7,7 @@ export * from './Notebooks'; export * from './Notifications'; export * from './Omics'; export * from './Pipelines'; +export * from './Redshift_Datasets'; export * from './S3_Datasets'; export * from './Shares'; export * from './Worksheets'; diff --git a/frontend/src/routes.js b/frontend/src/routes.js index 31679cef5..041763fdb 100644 --- a/frontend/src/routes.js +++ b/frontend/src/routes.js @@ -77,6 +77,26 @@ const FolderEditForm = Loadable( lazy(() => import('./modules/Folders/views/FolderEditForm')) ); +const RedshiftDatasetView = Loadable( + lazy(() => import('./modules/Redshift_Datasets/views/RSDatasetView')) +); + +const RedshiftDatasetImportForm = Loadable( + lazy(() => import('./modules/Redshift_Datasets/views/RSDatasetImportForm')) +); + +const RedshiftDatasetEditForm = Loadable( + lazy(() => import('./modules/Redshift_Datasets/views/RSDatasetEditForm')) +); + +const RedshiftTableView = Loadable( + lazy(() => import('./modules/Redshift_Datasets/views/RSTableView')) +); + +const RedshiftTableEditForm = Loadable( + lazy(() => import('./modules/Redshift_Datasets/views/RSTableEditForm')) +); + const NotebookList = Loadable( lazy(() => import('./modules/Notebooks/views/NotebookList')) ); @@ -292,6 +312,30 @@ const routes = [ } ] }, + isModuleEnabled(ModuleNames.REDSHIFT_DATASETS) && { + children: [ + { + path: 'redshift-datasets/:uri', + element: + }, + { + path: 'redshift-datasets/import', + element: + }, + { + path: 'redshift-datasets/:uri/edit', + element: + }, + { + path: 'redshift-datasets/table/:uri', + element: + }, + { + path: 'redshift-datasets/table/:uri/edit', + element: + } + ] + }, isModuleEnabled(ModuleNames.MLSTUDIO) && { children: [ { diff --git a/frontend/src/services/graphql/Redshift_Datasets/index.js b/frontend/src/services/graphql/Redshift_Datasets/index.js new file mode 100644 index 000000000..122a37d00 --- /dev/null +++ b/frontend/src/services/graphql/Redshift_Datasets/index.js @@ -0,0 +1 @@ +export * from './listEnvironmentRedshiftConnections'; diff --git a/frontend/src/services/graphql/Redshift_Datasets/listEnvironmentRedshiftConnections.js b/frontend/src/services/graphql/Redshift_Datasets/listEnvironmentRedshiftConnections.js new file mode 100644 index 000000000..e2e1d2199 --- /dev/null +++ b/frontend/src/services/graphql/Redshift_Datasets/listEnvironmentRedshiftConnections.js @@ -0,0 +1,30 @@ +import { gql } from 'apollo-boost'; + +export const listEnvironmentRedshiftConnections = ({ filter }) => ({ + variables: { + filter + }, + query: gql` + query listEnvironmentRedshiftConnections($filter: ConnectionFilter) { + listEnvironmentRedshiftConnections(filter: $filter) { + count + page + pages + hasNext + hasPrevious + nodes { + name + connectionUri + SamlGroupName + redshiftType + clusterId + nameSpaceId + workgroup + database + redshiftUser + secretArn + } + } + } + ` +}); diff --git a/frontend/src/services/graphql/Shared/getEnumByName.js b/frontend/src/services/graphql/Shared/getEnumByName.js new file mode 100644 index 000000000..f4345544b --- /dev/null +++ b/frontend/src/services/graphql/Shared/getEnumByName.js @@ -0,0 +1,36 @@ +import { gql } from 'apollo-boost'; + +const getEnumByName = ({ enum_names }) => ({ + variables: { + enum_names: enum_names + }, + query: gql` + query queryEnums($enum_names: [String]) { + queryEnums(enums_names: $enum_names) { + name + items { + name + value + } + } + } + ` +}); + +/// function to fetch multiple enums +/// output -- dictionary +// { +// 'enumName': [{name: '...', value: '..'}] +// } +export const fetchEnums = async (client, enum_names) => { + const response = await client.query( + getEnumByName({ enum_names: enum_names }) + ); + let enum_dict = {}; + if (!response.errors && response.data.queryEnums != null) { + response.data.queryEnums.map((x) => { + enum_dict[x.name] = x.items; + }); + } + return enum_dict; +}; diff --git a/frontend/src/services/graphql/Shared/index.js b/frontend/src/services/graphql/Shared/index.js new file mode 100644 index 000000000..af6921f17 --- /dev/null +++ b/frontend/src/services/graphql/Shared/index.js @@ -0,0 +1 @@ +export * from './getEnumByName'; diff --git a/frontend/src/services/graphql/index.js b/frontend/src/services/graphql/index.js index ce1c3fba2..c37eb2ef6 100644 --- a/frontend/src/services/graphql/index.js +++ b/frontend/src/services/graphql/index.js @@ -12,7 +12,9 @@ export * from './MLStudio'; export * from './Notification'; export * from './Organization'; export * from './Principal'; +export * from './Redshift_Datasets'; export * from './SavedQuery'; +export * from './Shared'; export * from './Search'; export * from './ShareObject'; export * from './Stack'; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index be8b503b7..8930f8131 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@adobe/css-tools@4.3.3": +"@adobe/css-tools@4.3.3", "@adobe/css-tools@^4.0.1": version "4.3.3" resolved "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz" integrity sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ== @@ -289,15 +289,6 @@ fast-xml-parser "^4.2.5" tslib "^1.8.0" -"@aws-crypto/crc32@^1.0.0": - version "1.2.2" - resolved "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-1.2.2.tgz" - integrity sha512-8K0b1672qbv05chSoKpwGZ3fhvVp28Fg3AVHVkEHFl2lTLChO7wD/hTyyo8ING7uc31uZRt7bNra/hA74Td7Tw== - dependencies: - "@aws-crypto/util" "^1.2.2" - "@aws-sdk/types" "^3.1.0" - tslib "^1.11.1" - "@aws-crypto/crc32@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-2.0.0.tgz" @@ -307,6 +298,15 @@ "@aws-sdk/types" "^3.1.0" tslib "^1.11.1" +"@aws-crypto/crc32@^1.0.0": + version "1.2.2" + resolved "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-1.2.2.tgz" + integrity sha512-8K0b1672qbv05chSoKpwGZ3fhvVp28Fg3AVHVkEHFl2lTLChO7wD/hTyyo8ING7uc31uZRt7bNra/hA74Td7Tw== + dependencies: + "@aws-crypto/util" "^1.2.2" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + "@aws-crypto/ie11-detection@^1.0.0": version "1.0.0" resolved "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-1.0.0.tgz" @@ -321,19 +321,6 @@ dependencies: tslib "^1.11.1" -"@aws-crypto/sha256-browser@^1.0.0": - version "1.2.2" - resolved "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-1.2.2.tgz" - integrity sha512-0tNR4kBtJp+9S0kis4+JLab3eg6QWuIeuPhzaYoYwNUXGBgsWIkktA2mnilet+EGWzf3n1zknJXC4X4DVyyXbg== - dependencies: - "@aws-crypto/ie11-detection" "^1.0.0" - "@aws-crypto/sha256-js" "^1.2.2" - "@aws-crypto/supports-web-crypto" "^1.0.0" - "@aws-crypto/util" "^1.2.2" - "@aws-sdk/types" "^3.1.0" - "@aws-sdk/util-locate-window" "^3.0.0" - tslib "^1.11.1" - "@aws-crypto/sha256-browser@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz" @@ -348,7 +335,20 @@ "@aws-sdk/util-utf8-browser" "^3.0.0" tslib "^1.11.1" -"@aws-crypto/sha256-js@^1.0.0", "@aws-crypto/sha256-js@^1.2.2", "@aws-crypto/sha256-js@1.2.2": +"@aws-crypto/sha256-browser@^1.0.0": + version "1.2.2" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-1.2.2.tgz" + integrity sha512-0tNR4kBtJp+9S0kis4+JLab3eg6QWuIeuPhzaYoYwNUXGBgsWIkktA2mnilet+EGWzf3n1zknJXC4X4DVyyXbg== + dependencies: + "@aws-crypto/ie11-detection" "^1.0.0" + "@aws-crypto/sha256-js" "^1.2.2" + "@aws-crypto/supports-web-crypto" "^1.0.0" + "@aws-crypto/util" "^1.2.2" + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-locate-window" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-js@1.2.2", "@aws-crypto/sha256-js@^1.0.0", "@aws-crypto/sha256-js@^1.2.2": version "1.2.2" resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-1.2.2.tgz" integrity sha512-Nr1QJIbW/afYYGzYvrF70LtaHrIRtd4TNAglX8BvlfxJLZ45SAmueIKYl5tWoNBPzp65ymXGFK0Bb1vZUpuc9g== @@ -357,7 +357,7 @@ "@aws-sdk/types" "^3.1.0" tslib "^1.11.1" -"@aws-crypto/sha256-js@^2.0.0", "@aws-crypto/sha256-js@2.0.0": +"@aws-crypto/sha256-js@2.0.0", "@aws-crypto/sha256-js@^2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz" integrity sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig== @@ -1641,7 +1641,12 @@ "@aws-sdk/types" "3.6.1" tslib "^1.8.0" -"@aws-sdk/types@^3.1.0", "@aws-sdk/types@3.6.1": +"@aws-sdk/types@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.186.0.tgz" + integrity sha512-NatmSU37U+XauMFJCdFI6nougC20JUFZar+ump5wVv0i54H+2Refg1YbFDxSs0FY28TSB9jfhWIpfFBmXgL5MQ== + +"@aws-sdk/types@3.6.1", "@aws-sdk/types@^3.1.0": version "3.6.1" resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.6.1.tgz" integrity sha512-4Dx3eRTrUHLxhFdLJL8zdNGzVsJfAxtxPYYGmIddUkO2Gj3WA1TGjdfG4XN/ClI6e1XonCHafQX3UYO/mgnH3g== @@ -1654,11 +1659,6 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" -"@aws-sdk/types@3.186.0": - version "3.186.0" - resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.186.0.tgz" - integrity sha512-NatmSU37U+XauMFJCdFI6nougC20JUFZar+ump5wVv0i54H+2Refg1YbFDxSs0FY28TSB9jfhWIpfFBmXgL5MQ== - "@aws-sdk/url-parser-native@3.6.1": version "3.6.1" resolved "https://registry.npmjs.org/@aws-sdk/url-parser-native/-/url-parser-native-3.6.1.tgz" @@ -1868,13 +1868,6 @@ "@aws-sdk/types" "3.6.1" tslib "^1.8.0" -"@aws-sdk/util-utf8-browser@^3.0.0", "@aws-sdk/util-utf8-browser@3.6.1": - version "3.6.1" - resolved "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.6.1.tgz" - integrity sha512-gZPySY6JU5gswnw3nGOEHl3tYE7vPKvtXGYoS2NRabfDKRejFvu+4/nNW6SSpoOxk6LSXsrWB39NO51k+G4PVA== - dependencies: - tslib "^1.8.0" - "@aws-sdk/util-utf8-browser@3.186.0": version "3.186.0" resolved "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.186.0.tgz" @@ -1882,6 +1875,13 @@ dependencies: tslib "^2.3.1" +"@aws-sdk/util-utf8-browser@3.6.1", "@aws-sdk/util-utf8-browser@^3.0.0": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.6.1.tgz" + integrity sha512-gZPySY6JU5gswnw3nGOEHl3tYE7vPKvtXGYoS2NRabfDKRejFvu+4/nNW6SSpoOxk6LSXsrWB39NO51k+G4PVA== + dependencies: + tslib "^1.8.0" + "@aws-sdk/util-utf8-node@3.186.0": version "3.186.0" resolved "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.186.0.tgz" @@ -1915,12 +1915,12 @@ "@babel/highlight" "^7.24.7" picocolors "^1.0.0" -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.24.7": +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz" integrity sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw== -"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.1.0", "@babel/core@^7.11.0", "@babel/core@^7.11.1", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.13.16", "@babel/core@^7.16.0", "@babel/core@^7.20.0", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": +"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": version "7.24.7" resolved "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz" integrity sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g== @@ -1950,7 +1950,7 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.1" -"@babel/generator@^7.20.0", "@babel/generator@^7.23.0", "@babel/generator@^7.24.7", "@babel/generator@^7.7.2": +"@babel/generator@^7.23.0", "@babel/generator@^7.24.7", "@babel/generator@^7.7.2": version "7.24.7" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz" integrity sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA== @@ -1975,7 +1975,7 @@ "@babel/traverse" "^7.24.7" "@babel/types" "^7.24.7" -"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.24.7": +"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz" integrity sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg== @@ -2021,7 +2021,7 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" -"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.20", "@babel/helper-environment-visitor@^7.24.7": +"@babel/helper-environment-visitor@^7.22.20", "@babel/helper-environment-visitor@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz" integrity sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ== @@ -2082,7 +2082,7 @@ resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz" integrity sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg== -"@babel/helper-remap-async-to-generator@^7.18.9", "@babel/helper-remap-async-to-generator@^7.24.7": +"@babel/helper-remap-async-to-generator@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz" integrity sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA== @@ -2166,7 +2166,7 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.0", "@babel/parser@^7.24.7": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.0", "@babel/parser@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz" integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== @@ -2203,17 +2203,7 @@ "@babel/helper-environment-visitor" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-proposal-async-generator-functions@^7.0.0": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz" - integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.13.0", "@babel/plugin-proposal-class-properties@^7.16.0", "@babel/plugin-proposal-class-properties@^7.18.0": +"@babel/plugin-proposal-class-properties@^7.16.0": version "7.18.6" resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz" integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== @@ -2230,23 +2220,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-decorators" "^7.24.7" -"@babel/plugin-proposal-export-default-from@^7.0.0": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.24.7.tgz" - integrity sha512-CcmFwUJ3tKhLjPdt4NP+SHMshebytF8ZTYOv5ZDpkzq2sin80Wb5vJrGt8fhPrORQCfoSa0LAxC/DW+GAC5+Hw== - dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/plugin-syntax-export-default-from" "^7.24.7" - -"@babel/plugin-proposal-logical-assignment-operators@^7.18.0": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz" - integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.0": +"@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0": version "7.18.6" resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz" integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== @@ -2254,7 +2228,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-proposal-numeric-separator@^7.0.0", "@babel/plugin-proposal-numeric-separator@^7.16.0": +"@babel/plugin-proposal-numeric-separator@^7.16.0": version "7.18.6" resolved "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz" integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== @@ -2262,26 +2236,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.20.0": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz" - integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.7" - -"@babel/plugin-proposal-optional-catch-binding@^7.0.0": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz" - integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.16.0", "@babel/plugin-proposal-optional-chaining@^7.20.0": +"@babel/plugin-proposal-optional-chaining@^7.16.0": version "7.21.0" resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz" integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== @@ -2338,20 +2293,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": +"@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.24.7": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.24.7.tgz" - integrity sha512-bTPz4/635WQ9WhwsyPdxUJDVpsi/X9BMmy/8Rf/UAlOO4jSql4CxUCjWI5PiM+jG+c4LVPTScoTw80geFj9+Bw== - dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/plugin-syntax-export-namespace-from@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz" @@ -2359,7 +2307,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-flow@^7.12.1", "@babel/plugin-syntax-flow@^7.14.5", "@babel/plugin-syntax-flow@^7.18.0", "@babel/plugin-syntax-flow@^7.24.7": +"@babel/plugin-syntax-flow@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.7.tgz" integrity sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw== @@ -2408,7 +2356,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-nullish-coalescing-operator@^7.0.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== @@ -2436,7 +2384,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-optional-chaining@^7.0.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": +"@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== @@ -2472,7 +2420,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-arrow-functions@^7.0.0", "@babel/plugin-transform-arrow-functions@^7.24.7": +"@babel/plugin-transform-arrow-functions@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz" integrity sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ== @@ -2489,7 +2437,7 @@ "@babel/helper-remap-async-to-generator" "^7.24.7" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-transform-async-to-generator@^7.20.0", "@babel/plugin-transform-async-to-generator@^7.24.7": +"@babel/plugin-transform-async-to-generator@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz" integrity sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA== @@ -2505,7 +2453,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-block-scoping@^7.0.0", "@babel/plugin-transform-block-scoping@^7.24.7": +"@babel/plugin-transform-block-scoping@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz" integrity sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ== @@ -2529,7 +2477,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-transform-classes@^7.0.0", "@babel/plugin-transform-classes@^7.24.7": +"@babel/plugin-transform-classes@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz" integrity sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw== @@ -2543,7 +2491,7 @@ "@babel/helper-split-export-declaration" "^7.24.7" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.0.0", "@babel/plugin-transform-computed-properties@^7.24.7": +"@babel/plugin-transform-computed-properties@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz" integrity sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ== @@ -2551,7 +2499,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/template" "^7.24.7" -"@babel/plugin-transform-destructuring@^7.20.0", "@babel/plugin-transform-destructuring@^7.24.7": +"@babel/plugin-transform-destructuring@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz" integrity sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw== @@ -2597,7 +2545,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-transform-flow-strip-types@^7.16.0", "@babel/plugin-transform-flow-strip-types@^7.20.0", "@babel/plugin-transform-flow-strip-types@^7.24.7": +"@babel/plugin-transform-flow-strip-types@^7.16.0": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.7.tgz" integrity sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA== @@ -2613,7 +2561,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" -"@babel/plugin-transform-function-name@^7.0.0", "@babel/plugin-transform-function-name@^7.24.7": +"@babel/plugin-transform-function-name@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz" integrity sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w== @@ -2630,7 +2578,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-transform-literals@^7.0.0", "@babel/plugin-transform-literals@^7.24.7": +"@babel/plugin-transform-literals@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz" integrity sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ== @@ -2660,7 +2608,7 @@ "@babel/helper-module-transforms" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.24.7": +"@babel/plugin-transform-modules-commonjs@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz" integrity sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ== @@ -2687,7 +2635,7 @@ "@babel/helper-module-transforms" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-named-capturing-groups-regex@^7.0.0", "@babel/plugin-transform-named-capturing-groups-regex@^7.24.7": +"@babel/plugin-transform-named-capturing-groups-regex@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz" integrity sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g== @@ -2753,14 +2701,14 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.24.7": +"@babel/plugin-transform-parameters@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz" integrity sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA== dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-private-methods@^7.22.5", "@babel/plugin-transform-private-methods@^7.24.7": +"@babel/plugin-transform-private-methods@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz" integrity sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ== @@ -2768,7 +2716,7 @@ "@babel/helper-create-class-features-plugin" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-private-property-in-object@^7.22.11", "@babel/plugin-transform-private-property-in-object@^7.24.7": +"@babel/plugin-transform-private-property-in-object@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz" integrity sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA== @@ -2792,7 +2740,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-react-display-name@^7.0.0", "@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.24.7": +"@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz" integrity sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg== @@ -2806,21 +2754,7 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.24.7" -"@babel/plugin-transform-react-jsx-self@^7.0.0": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz" - integrity sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw== - dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - -"@babel/plugin-transform-react-jsx-source@^7.0.0": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz" - integrity sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - -"@babel/plugin-transform-react-jsx@^7.0.0", "@babel/plugin-transform-react-jsx@^7.14.9", "@babel/plugin-transform-react-jsx@^7.24.7": +"@babel/plugin-transform-react-jsx@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz" integrity sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA== @@ -2854,7 +2788,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-runtime@^7.0.0", "@babel/plugin-transform-runtime@^7.16.4": +"@babel/plugin-transform-runtime@^7.16.4": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz" integrity sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw== @@ -2866,14 +2800,14 @@ babel-plugin-polyfill-regenerator "^0.6.1" semver "^6.3.1" -"@babel/plugin-transform-shorthand-properties@^7.0.0", "@babel/plugin-transform-shorthand-properties@^7.24.7": +"@babel/plugin-transform-shorthand-properties@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz" integrity sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA== dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-spread@^7.0.0", "@babel/plugin-transform-spread@^7.24.7": +"@babel/plugin-transform-spread@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz" integrity sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng== @@ -2881,7 +2815,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" -"@babel/plugin-transform-sticky-regex@^7.0.0", "@babel/plugin-transform-sticky-regex@^7.24.7": +"@babel/plugin-transform-sticky-regex@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz" integrity sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g== @@ -2902,7 +2836,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-typescript@^7.24.7", "@babel/plugin-transform-typescript@^7.5.0": +"@babel/plugin-transform-typescript@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.7.tgz" integrity sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw== @@ -2927,7 +2861,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-unicode-regex@^7.0.0", "@babel/plugin-transform-unicode-regex@^7.24.7": +"@babel/plugin-transform-unicode-regex@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz" integrity sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg== @@ -2943,7 +2877,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/preset-env@^7.1.6", "@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4": +"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4": version "7.24.7" resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz" integrity sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ== @@ -3030,15 +2964,6 @@ core-js-compat "^3.31.0" semver "^6.3.1" -"@babel/preset-flow@^7.13.13": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.24.7.tgz" - integrity sha512-NL3Lo0NorCU607zU3NwRyJbpaB6E3t0xtd3LfAQKDfkeX4/ggcDXvkmkW42QWT5owUeW/jAe4hn+2qvkV1IbfQ== - dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/helper-validator-option" "^7.24.7" - "@babel/plugin-transform-flow-strip-types" "^7.24.7" - "@babel/preset-modules@0.1.6-no-external-plugins": version "0.1.6-no-external-plugins" resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz" @@ -3060,7 +2985,7 @@ "@babel/plugin-transform-react-jsx-development" "^7.24.7" "@babel/plugin-transform-react-pure-annotations" "^7.24.7" -"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.16.0": +"@babel/preset-typescript@^7.16.0": version "7.24.7" resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz" integrity sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ== @@ -3071,30 +2996,19 @@ "@babel/plugin-transform-modules-commonjs" "^7.24.7" "@babel/plugin-transform-typescript" "^7.24.7" -"@babel/register@^7.13.16": - version "7.24.6" - resolved "https://registry.npmjs.org/@babel/register/-/register-7.24.6.tgz" - integrity sha512-WSuFCc2wCqMeXkz/i3yfAAsxwWflEgbVkZzivgAmXl/MxrXeoYFZOOPllbC8R8WTF7u61wSRQtDVZ1879cdu6w== - dependencies: - clone-deep "^4.0.1" - find-cache-dir "^2.0.0" - make-dir "^2.1.0" - pirates "^4.0.6" - source-map-support "^0.5.16" - "@babel/regjsgen@^0.8.0": version "0.8.0" resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.24.7" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz" integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.0.0", "@babel/template@^7.24.7", "@babel/template@^7.3.3": +"@babel/template@^7.24.7", "@babel/template@^7.3.3": version "7.24.7" resolved "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz" integrity sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig== @@ -3103,7 +3017,7 @@ "@babel/parser" "^7.24.7" "@babel/types" "^7.24.7" -"@babel/traverse@7.23.2": +"@babel/traverse@7.23.2", "@babel/traverse@^7.24.7", "@babel/traverse@^7.7.2": version "7.23.2" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz" integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== @@ -3119,7 +3033,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.23.0", "@babel/types@^7.24.7", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.20.7", "@babel/types@^7.23.0", "@babel/types@^7.24.7", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.24.7" resolved "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz" integrity sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q== @@ -3315,7 +3229,7 @@ "@emotion/weak-memoize" "^0.3.1" stylis "4.2.0" -"@emotion/core@^10.0.27", "@emotion/core@^10.0.28": +"@emotion/core@^10.0.28": version "10.3.1" resolved "https://registry.npmjs.org/@emotion/core/-/core-10.3.1.tgz" integrity sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww== @@ -3336,22 +3250,15 @@ "@emotion/utils" "0.11.3" babel-plugin-emotion "^10.0.27" -"@emotion/hash@^0.9.1": - version "0.9.1" - resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz" - integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== - "@emotion/hash@0.8.0": version "0.8.0" resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== -"@emotion/is-prop-valid@^1.2.2": - version "1.2.2" - resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz" - integrity sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw== - dependencies: - "@emotion/memoize" "^0.8.1" +"@emotion/hash@^0.9.1": + version "0.9.1" + resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz" + integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== "@emotion/is-prop-valid@0.8.8": version "0.8.8" @@ -3360,17 +3267,24 @@ dependencies: "@emotion/memoize" "0.7.4" -"@emotion/memoize@^0.8.1": - version "0.8.1" - resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz" - integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== +"@emotion/is-prop-valid@^1.2.2": + version "1.2.2" + resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz" + integrity sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw== + dependencies: + "@emotion/memoize" "^0.8.1" "@emotion/memoize@0.7.4": version "0.7.4" resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz" integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== -"@emotion/react@^11.0.0-rc.0", "@emotion/react@^11.4.1", "@emotion/react@^11.5.0", "@emotion/react@^11.8.2", "@emotion/react@^11.9.0": +"@emotion/memoize@^0.8.1": + version "0.8.1" + resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz" + integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== + +"@emotion/react@^11.8.2": version "11.11.4" resolved "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz" integrity sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw== @@ -3395,18 +3309,7 @@ "@emotion/utils" "0.11.3" csstype "^2.5.7" -"@emotion/serialize@^1.1.2": - version "1.1.4" - resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz" - integrity sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ== - dependencies: - "@emotion/hash" "^0.9.1" - "@emotion/memoize" "^0.8.1" - "@emotion/unitless" "^0.8.1" - "@emotion/utils" "^1.2.1" - csstype "^3.0.2" - -"@emotion/serialize@^1.1.3": +"@emotion/serialize@^1.1.2", "@emotion/serialize@^1.1.3", "@emotion/serialize@^1.1.4": version "1.1.4" resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz" integrity sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ== @@ -3417,27 +3320,16 @@ "@emotion/utils" "^1.2.1" csstype "^3.0.2" -"@emotion/serialize@^1.1.4": - version "1.1.4" - resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz" - integrity sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ== - dependencies: - "@emotion/hash" "^0.9.1" - "@emotion/memoize" "^0.8.1" - "@emotion/unitless" "^0.8.1" - "@emotion/utils" "^1.2.1" - csstype "^3.0.2" +"@emotion/sheet@0.9.4": + version "0.9.4" + resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz" + integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA== "@emotion/sheet@^1.2.2": version "1.2.2" resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz" integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== -"@emotion/sheet@0.9.4": - version "0.9.4" - resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz" - integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA== - "@emotion/styled-base@^10.3.0": version "10.3.0" resolved "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.3.0.tgz" @@ -3456,7 +3348,7 @@ "@emotion/styled-base" "^10.3.0" babel-plugin-emotion "^10.0.27" -"@emotion/styled@^11.3.0", "@emotion/styled@^11.8.1": +"@emotion/styled@^11.8.1": version "11.11.5" resolved "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz" integrity sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ== @@ -3473,41 +3365,41 @@ resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz" integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== -"@emotion/unitless@^0.8.1": - version "0.8.1" - resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz" - integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== - "@emotion/unitless@0.7.5": version "0.7.5" resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== +"@emotion/unitless@^0.8.1": + version "0.8.1" + resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz" + integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== + "@emotion/use-insertion-effect-with-fallbacks@^1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz" integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw== -"@emotion/utils@^1.2.1": - version "1.2.1" - resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz" - integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg== - "@emotion/utils@0.11.3": version "0.11.3" resolved "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz" integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw== -"@emotion/weak-memoize@^0.3.1": - version "0.3.1" - resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz" - integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== +"@emotion/utils@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz" + integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg== "@emotion/weak-memoize@0.2.5": version "0.2.5" resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== +"@emotion/weak-memoize@^0.3.1": + version "0.3.1" + resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz" + integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" @@ -3572,18 +3464,6 @@ resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== -"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": - version "9.3.0" - resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz" - integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== - -"@hapi/topo@^5.1.0": - version "5.1.0" - resolved "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz" - integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== - dependencies: - "@hapi/hoek" "^9.0.0" - "@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz" @@ -3615,11 +3495,6 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" -"@isaacs/ttlcache@^1.4.1": - version "1.4.1" - resolved "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz" - integrity sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA== - "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" @@ -3694,13 +3569,6 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/create-cache-key-function@^29.6.3": - version "29.7.0" - resolved "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz" - integrity sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA== - dependencies: - "@jest/types" "^29.6.3" - "@jest/environment@^27.5.1": version "27.5.1" resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz" @@ -3711,16 +3579,6 @@ "@types/node" "*" jest-mock "^27.5.1" -"@jest/environment@^29.7.0": - version "29.7.0" - resolved "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz" - integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== - dependencies: - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - "@jest/expect-utils@^29.7.0": version "29.7.0" resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz" @@ -3740,18 +3598,6 @@ jest-mock "^27.5.1" jest-util "^27.5.1" -"@jest/fake-timers@^29.7.0": - version "29.7.0" - resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz" - integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== - dependencies: - "@jest/types" "^29.6.3" - "@sinonjs/fake-timers" "^10.0.2" - "@types/node" "*" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-util "^29.7.0" - "@jest/globals@^27.5.1": version "27.5.1" resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz" @@ -3866,17 +3712,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/types@^26.6.2": - version "26.6.2" - resolved "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz" - integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - "@jest/types@^27.5.1": version "27.5.1" resolved "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz" @@ -3976,7 +3811,7 @@ dependencies: "@monaco-editor/loader" "^1.4.0" -"@mui/base@^5.0.0-beta.20", "@mui/base@5.0.0-beta.40": +"@mui/base@5.0.0-beta.40", "@mui/base@^5.0.0-beta.20": version "5.0.0-beta.40" resolved "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz" integrity sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ== @@ -4014,7 +3849,7 @@ clsx "^2.1.0" prop-types "^15.8.1" -"@mui/material@^5.0.0", "@mui/material@^5.4.1", "@mui/material@^5.5.2", "@mui/material@^5.8.6", "@mui/material@>=5.15.0": +"@mui/material@^5.5.2": version "5.15.19" resolved "https://registry.npmjs.org/@mui/material/-/material-5.15.19.tgz" integrity sha512-lp5xQBbcRuxNtjpWU0BWZgIrv2XLUz4RJ0RqFXBdESIsKoGCQZ6P3wwU5ZPuj5TjssNiKv9AlM+vHopRxZhvVQ== @@ -4074,7 +3909,7 @@ jss-plugin-vendor-prefixer "^10.10.0" prop-types "^15.8.1" -"@mui/system@^5.15.15", "@mui/system@^5.4.1", "@mui/system@^5.8.0": +"@mui/system@^5.15.15": version "5.15.15" resolved "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz" integrity sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ== @@ -4160,7 +3995,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -4196,310 +4031,6 @@ resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== -"@react-native-community/cli-clean@13.6.8": - version "13.6.8" - resolved "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-13.6.8.tgz" - integrity sha512-B1uxlm1N4BQuWFvBL3yRl3LVvydjswsdbTi7tMrHMtSxfRio1p9HjcmDzlzKco09Y+8qBGgakm3jcMZGLbhXQQ== - dependencies: - "@react-native-community/cli-tools" "13.6.8" - chalk "^4.1.2" - execa "^5.0.0" - fast-glob "^3.3.2" - -"@react-native-community/cli-config@13.6.8": - version "13.6.8" - resolved "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-13.6.8.tgz" - integrity sha512-RabCkIsWdP4Ex/sf1uSP9qxc30utm+0uIJAjrZkNQynm7T4Lyqn/kT3LKm4yM6M0Qk61YxGguiaXF4601vAduw== - dependencies: - "@react-native-community/cli-tools" "13.6.8" - chalk "^4.1.2" - cosmiconfig "^5.1.0" - deepmerge "^4.3.0" - fast-glob "^3.3.2" - joi "^17.2.1" - -"@react-native-community/cli-debugger-ui@13.6.8": - version "13.6.8" - resolved "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-13.6.8.tgz" - integrity sha512-2cS+MX/Su6sVSjqpDftFOXbK7EuPg98xzsPkdPhkQnkZwvXqodK9CAMuDMbx3lBHHtrPrpMbBCpFmPN8iVOnlA== - dependencies: - serve-static "^1.13.1" - -"@react-native-community/cli-doctor@13.6.8": - version "13.6.8" - resolved "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-13.6.8.tgz" - integrity sha512-/3Vdy9J3hyiu0y3nd/CU3kBqPlTRxnLXg7V6jrA1jbTOlZAMyV9imEkrqEaGK0SMOyMhh9Pipf98Ozhk0Nl4QA== - dependencies: - "@react-native-community/cli-config" "13.6.8" - "@react-native-community/cli-platform-android" "13.6.8" - "@react-native-community/cli-platform-apple" "13.6.8" - "@react-native-community/cli-platform-ios" "13.6.8" - "@react-native-community/cli-tools" "13.6.8" - chalk "^4.1.2" - command-exists "^1.2.8" - deepmerge "^4.3.0" - envinfo "^7.10.0" - execa "^5.0.0" - hermes-profile-transformer "^0.0.6" - node-stream-zip "^1.9.1" - ora "^5.4.1" - semver "^7.5.2" - strip-ansi "^5.2.0" - wcwidth "^1.0.1" - yaml "^2.2.1" - -"@react-native-community/cli-hermes@13.6.8": - version "13.6.8" - resolved "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-13.6.8.tgz" - integrity sha512-lZi/OBFuZUj5cLK94oEgtrtmxGoqeYVRcnHXl/R5c4put9PDl+qH2bEMlGZkFiw57ae3UZKr3TMk+1s4jh3FYQ== - dependencies: - "@react-native-community/cli-platform-android" "13.6.8" - "@react-native-community/cli-tools" "13.6.8" - chalk "^4.1.2" - hermes-profile-transformer "^0.0.6" - -"@react-native-community/cli-platform-android@13.6.8": - version "13.6.8" - resolved "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-13.6.8.tgz" - integrity sha512-vWrqeLRRTwp2kO33nbrAgbYn8HR2c2CpIfyVJY9Ckk7HGUSwDyxdcSu7YBvt2ShdfLZH0HctWFNXsgGrfg6BDw== - dependencies: - "@react-native-community/cli-tools" "13.6.8" - chalk "^4.1.2" - execa "^5.0.0" - fast-glob "^3.3.2" - fast-xml-parser "^4.2.4" - logkitty "^0.7.1" - -"@react-native-community/cli-platform-apple@13.6.8": - version "13.6.8" - resolved "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-13.6.8.tgz" - integrity sha512-1JPohnlXPqU44zns3ALEzIbH2cKRw6JtEDJERgLuEUbs2r2NeJgqDbKyZ7fTTO8o+pegDnn6+Rr7qGVVOuUzzg== - dependencies: - "@react-native-community/cli-tools" "13.6.8" - chalk "^4.1.2" - execa "^5.0.0" - fast-glob "^3.3.2" - fast-xml-parser "^4.0.12" - ora "^5.4.1" - -"@react-native-community/cli-platform-ios@13.6.8": - version "13.6.8" - resolved "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-13.6.8.tgz" - integrity sha512-/IIcIRM8qaoD7iZqsvtf6Qq1AwtChWYfB9sTn3mTiolZ5Zd5bXH37g+6liPfAICRkj2Ptq3iXmjrDVUQAxrOXw== - dependencies: - "@react-native-community/cli-platform-apple" "13.6.8" - -"@react-native-community/cli-server-api@13.6.8": - version "13.6.8" - resolved "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-13.6.8.tgz" - integrity sha512-Lx664oWTzpVfbKUTy+3GIX7e+Mt5Zn+zdkM4ehllNdik/lbB3tM9Nrg8PSvOfI+tTXs2w55+nIydLfH+0FqJVg== - dependencies: - "@react-native-community/cli-debugger-ui" "13.6.8" - "@react-native-community/cli-tools" "13.6.8" - compression "^1.7.1" - connect "^3.6.5" - errorhandler "^1.5.1" - nocache "^3.0.1" - pretty-format "^26.6.2" - serve-static "^1.13.1" - ws "^6.2.2" - -"@react-native-community/cli-tools@13.6.8": - version "13.6.8" - resolved "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-13.6.8.tgz" - integrity sha512-1MYlae9EkbjC7DBYOGMH5xF9yDoeNYUKgEdDjL6WAUBoF2gtwiZPM6igLKi/+dhb5sCtC7fiLrLi0Oevdf+RmQ== - dependencies: - appdirsjs "^1.2.4" - chalk "^4.1.2" - execa "^5.0.0" - find-up "^5.0.0" - mime "^2.4.1" - node-fetch "^2.6.0" - open "^6.2.0" - ora "^5.4.1" - semver "^7.5.2" - shell-quote "^1.7.3" - sudo-prompt "^9.0.0" - -"@react-native-community/cli-types@13.6.8": - version "13.6.8" - resolved "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-13.6.8.tgz" - integrity sha512-C4mVByy0i+/NPuPhdMLBR7ubEVkjVS1VwoQu/BoG1crJFNE+167QXAzH01eFbXndsjZaMWmD4Gerx7TYc6lHfA== - dependencies: - joi "^17.2.1" - -"@react-native-community/cli@13.6.8": - version "13.6.8" - resolved "https://registry.npmjs.org/@react-native-community/cli/-/cli-13.6.8.tgz" - integrity sha512-0lRdgLNaXixWY4BfFRl1J6Ao9Lapo2z+++iE7TD4GAbuxOWJSyFi+KUA8XNfSDyML4jFO02MZgyBPxAWdaminQ== - dependencies: - "@react-native-community/cli-clean" "13.6.8" - "@react-native-community/cli-config" "13.6.8" - "@react-native-community/cli-debugger-ui" "13.6.8" - "@react-native-community/cli-doctor" "13.6.8" - "@react-native-community/cli-hermes" "13.6.8" - "@react-native-community/cli-server-api" "13.6.8" - "@react-native-community/cli-tools" "13.6.8" - "@react-native-community/cli-types" "13.6.8" - chalk "^4.1.2" - commander "^9.4.1" - deepmerge "^4.3.0" - execa "^5.0.0" - find-up "^4.1.0" - fs-extra "^8.1.0" - graceful-fs "^4.1.3" - prompts "^2.4.2" - semver "^7.5.2" - -"@react-native/assets-registry@0.74.84": - version "0.74.84" - resolved "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.84.tgz" - integrity sha512-dzUhwyaX04QosWZ8zyaaNB/WYZIdeDN1lcpfQbqiOhZJShRH+FLTDVONE/dqlMQrP+EO7lDqF0RrlIt9lnOCQQ== - -"@react-native/babel-plugin-codegen@0.74.84": - version "0.74.84" - resolved "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.84.tgz" - integrity sha512-UR4uiii5szIJA84mSC6GJOfYKDq7/ThyetOQT62+BBcyGeHVtHlNLNRzgaMeLqIQaT8Fq4pccMI+7QqLOMXzdw== - dependencies: - "@react-native/codegen" "0.74.84" - -"@react-native/babel-preset@0.74.84": - version "0.74.84" - resolved "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.84.tgz" - integrity sha512-WUfu6Y4aGuVdocQZvx33BJiQWFH6kRCHYbZfBn2psgFrSRLgQWEQrDCxqPFObNAVSayM0rNhp2FvI5K/Eyeqlg== - dependencies: - "@babel/core" "^7.20.0" - "@babel/plugin-proposal-async-generator-functions" "^7.0.0" - "@babel/plugin-proposal-class-properties" "^7.18.0" - "@babel/plugin-proposal-export-default-from" "^7.0.0" - "@babel/plugin-proposal-logical-assignment-operators" "^7.18.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.0" - "@babel/plugin-proposal-numeric-separator" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.20.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" - "@babel/plugin-proposal-optional-chaining" "^7.20.0" - "@babel/plugin-syntax-dynamic-import" "^7.8.0" - "@babel/plugin-syntax-export-default-from" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.18.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-syntax-optional-chaining" "^7.0.0" - "@babel/plugin-transform-arrow-functions" "^7.0.0" - "@babel/plugin-transform-async-to-generator" "^7.20.0" - "@babel/plugin-transform-block-scoping" "^7.0.0" - "@babel/plugin-transform-classes" "^7.0.0" - "@babel/plugin-transform-computed-properties" "^7.0.0" - "@babel/plugin-transform-destructuring" "^7.20.0" - "@babel/plugin-transform-flow-strip-types" "^7.20.0" - "@babel/plugin-transform-function-name" "^7.0.0" - "@babel/plugin-transform-literals" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.0.0" - "@babel/plugin-transform-parameters" "^7.0.0" - "@babel/plugin-transform-private-methods" "^7.22.5" - "@babel/plugin-transform-private-property-in-object" "^7.22.11" - "@babel/plugin-transform-react-display-name" "^7.0.0" - "@babel/plugin-transform-react-jsx" "^7.0.0" - "@babel/plugin-transform-react-jsx-self" "^7.0.0" - "@babel/plugin-transform-react-jsx-source" "^7.0.0" - "@babel/plugin-transform-runtime" "^7.0.0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0" - "@babel/plugin-transform-spread" "^7.0.0" - "@babel/plugin-transform-sticky-regex" "^7.0.0" - "@babel/plugin-transform-typescript" "^7.5.0" - "@babel/plugin-transform-unicode-regex" "^7.0.0" - "@babel/template" "^7.0.0" - "@react-native/babel-plugin-codegen" "0.74.84" - babel-plugin-transform-flow-enums "^0.0.2" - react-refresh "^0.14.0" - -"@react-native/codegen@0.74.84": - version "0.74.84" - resolved "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.84.tgz" - integrity sha512-0hXlnu9i0o8v+gXKQi+x6T471L85kCDwW4WrJiYAeOheWrQdNNW6rC3g8+LL7HXAf7QcHGU/8/d57iYfdVK2BQ== - dependencies: - "@babel/parser" "^7.20.0" - glob "^7.1.1" - hermes-parser "0.19.1" - invariant "^2.2.4" - jscodeshift "^0.14.0" - mkdirp "^0.5.1" - nullthrows "^1.1.1" - -"@react-native/community-cli-plugin@0.74.84": - version "0.74.84" - resolved "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.84.tgz" - integrity sha512-GBKE+1sUh86fS2XXV46gMCNHMc1KetshMbYJ0AhDhldpaILZHqRBX50mdVsiYVvkzp4QjM0nmYqefuJ9NVwicQ== - dependencies: - "@react-native-community/cli-server-api" "13.6.8" - "@react-native-community/cli-tools" "13.6.8" - "@react-native/dev-middleware" "0.74.84" - "@react-native/metro-babel-transformer" "0.74.84" - chalk "^4.0.0" - execa "^5.1.1" - metro "^0.80.3" - metro-config "^0.80.3" - metro-core "^0.80.3" - node-fetch "^2.2.0" - querystring "^0.2.1" - readline "^1.3.0" - -"@react-native/debugger-frontend@0.74.84": - version "0.74.84" - resolved "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.84.tgz" - integrity sha512-YUEA03UNFbiYzHpYxlcS2D9+3eNT5YLGkl5yRg3nOSN6KbCc/OttGnNZme+tuSOJwjMN/vcvtDKYkTqjJw8U0A== - -"@react-native/dev-middleware@0.74.84": - version "0.74.84" - resolved "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.84.tgz" - integrity sha512-veYw/WmyrAOQHUiIeULzn2duJQnXDPiKq2jZ/lcmDo6jsLirpp+Q73lx09TYgy/oVoPRuV0nfmU3x9B6EV/7qQ== - dependencies: - "@isaacs/ttlcache" "^1.4.1" - "@react-native/debugger-frontend" "0.74.84" - "@rnx-kit/chromium-edge-launcher" "^1.0.0" - chrome-launcher "^0.15.2" - connect "^3.6.5" - debug "^2.2.0" - node-fetch "^2.2.0" - nullthrows "^1.1.1" - open "^7.0.3" - selfsigned "^2.4.1" - serve-static "^1.13.1" - temp-dir "^2.0.0" - ws "^6.2.2" - -"@react-native/gradle-plugin@0.74.84": - version "0.74.84" - resolved "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.84.tgz" - integrity sha512-wYWC5WWXqzCCe4PDogz9pNc4xH5ZamahW5XGSbrrYJ5V3walZ+7z43V6iEBJkZbLjj9YBcSttkXYGr1Xh4veAg== - -"@react-native/js-polyfills@0.74.84": - version "0.74.84" - resolved "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.84.tgz" - integrity sha512-+PgxuUjBw9JVlz6m4ECsIJMLbDopnr4rpLmsG32hQaJrg0wMuvHtsgAY/J/aVCSG2GNUXexfjrnhc+O9yGOZXQ== - -"@react-native/metro-babel-transformer@0.74.84": - version "0.74.84" - resolved "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.84.tgz" - integrity sha512-YtVGq7jkgyUECv5yt4BOFbOXyW4ddUn8+dnwGGpJKdfhXYL5o5++AxNdE+2x+SZdkj3JUVekGKPwRabFECABaw== - dependencies: - "@babel/core" "^7.20.0" - "@react-native/babel-preset" "0.74.84" - hermes-parser "0.19.1" - nullthrows "^1.1.1" - -"@react-native/normalize-colors@0.74.84": - version "0.74.84" - resolved "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.84.tgz" - integrity sha512-Y5W6x8cC5RuakUcTVUFNAIhUZ/tYpuqHZlRBoAuakrTwVuoNHXfQki8lj1KsYU7rW6e3VWgdEx33AfOQpdNp6A== - -"@react-native/virtualized-lists@0.74.84": - version "0.74.84" - resolved "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.84.tgz" - integrity sha512-XcV+qdqt2WihaY4iRm/M1FdSy+18lecU9mRXNmy9YK8g9Th/8XbNtmmKI0qWBx3KxyuXMH/zd0ps05YTrX16kw== - dependencies: - invariant "^2.2.4" - nullthrows "^1.1.1" - "@reduxjs/toolkit@^1.8.0": version "1.9.7" resolved "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz" @@ -4510,18 +4041,6 @@ redux-thunk "^2.4.2" reselect "^4.1.8" -"@rnx-kit/chromium-edge-launcher@^1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@rnx-kit/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz" - integrity sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg== - dependencies: - "@types/node" "^18.0.0" - escape-string-regexp "^4.0.0" - is-wsl "^2.2.0" - lighthouse-logger "^1.0.0" - mkdirp "^1.0.4" - rimraf "^3.0.2" - "@rollup/plugin-babel@^5.2.0": version "5.3.1" resolved "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz" @@ -4564,23 +4083,6 @@ resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz" integrity sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg== -"@sideway/address@^4.1.5": - version "4.1.5" - resolved "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz" - integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== - dependencies: - "@hapi/hoek" "^9.0.0" - -"@sideway/formula@^3.0.1": - version "3.0.1" - resolved "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz" - integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== - -"@sideway/pinpoint@^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz" - integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== - "@sinclair/typebox@^0.24.1": version "0.24.51" resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz" @@ -4598,20 +4100,6 @@ dependencies: type-detect "4.0.8" -"@sinonjs/commons@^3.0.0": - version "3.0.1" - resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz" - integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^10.0.2": - version "10.3.0" - resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz" - integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== - dependencies: - "@sinonjs/commons" "^3.0.0" - "@sinonjs/fake-timers@^8.0.1": version "8.1.0" resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz" @@ -4753,20 +4241,6 @@ lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/dom@>=7.21.4": - version "10.1.0" - resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-10.1.0.tgz" - integrity sha512-wdsYKy5zupPyLCW2Je5DLHSxSfbIp6h80WoHOQc+RPtmPGA52O9x5MJEkv92Sjonpq+poOAtUKhh1kBGAXBrNA== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/runtime" "^7.12.5" - "@types/aria-query" "^5.0.1" - aria-query "5.3.0" - chalk "^4.1.0" - dom-accessibility-api "^0.5.9" - lz-string "^1.5.0" - pretty-format "^27.0.2" - "@testing-library/jest-dom@^5.16.2": version "5.17.0" resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz" @@ -4833,7 +4307,7 @@ resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz" integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.9": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": version "7.20.5" resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== @@ -5048,13 +4522,6 @@ dependencies: undici-types "~5.26.4" -"@types/node@^18.0.0": - version "18.19.34" - resolved "https://registry.npmjs.org/@types/node/-/node-18.19.34.tgz" - integrity sha512-eXF4pfBNV5DAMKGbI02NnDtWrQ40hAN558/2vvS4gMpMIxaf6JmD7YjnZbq0Q9TDSSkKBamime8ewRoomHdt4g== - dependencies: - undici-types "~5.26.4" - "@types/parse-json@^4.0.0": version "4.0.2" resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz" @@ -5109,7 +4576,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^17", "@types/react@^17.0.0 || ^18.0.0": +"@types/react@*", "@types/react@^17": version "17.0.80" resolved "https://registry.npmjs.org/@types/react/-/react-17.0.80.tgz" integrity sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA== @@ -5118,14 +4585,6 @@ "@types/scheduler" "^0.16" csstype "^3.0.2" -"@types/react@^18.2.6": - version "18.3.3" - resolved "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz" - integrity sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz" @@ -5208,13 +4667,6 @@ resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== -"@types/yargs@^15.0.0": - version "15.0.19" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz" - integrity sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA== - dependencies: - "@types/yargs-parser" "*" - "@types/yargs@^16.0.0": version "16.0.9" resolved "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz" @@ -5234,7 +4686,7 @@ resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.7.tgz" integrity sha512-LKzNTjj+2j09wAo/vvVjzgw5qckJJzhdGgWHW7j69QIGdq/KnZrMAMIHQiWGl3Ccflh5/CudBAntTPYdprPltA== -"@typescript-eslint/eslint-plugin@^4.0.0 || ^5.0.0", "@typescript-eslint/eslint-plugin@^5.5.0": +"@typescript-eslint/eslint-plugin@^5.5.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz" integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== @@ -5257,7 +4709,7 @@ dependencies: "@typescript-eslint/utils" "5.62.0" -"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.5.0": +"@typescript-eslint/parser@^5.5.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz" integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== @@ -5303,7 +4755,7 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@^5.58.0", "@typescript-eslint/utils@5.62.0": +"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.58.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== @@ -5330,7 +4782,7 @@ resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@webassemblyjs/ast@^1.12.1", "@webassemblyjs/ast@1.12.1": +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": version "1.12.1" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz" integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== @@ -5431,7 +4883,7 @@ "@webassemblyjs/wasm-gen" "1.12.1" "@webassemblyjs/wasm-parser" "1.12.1" -"@webassemblyjs/wasm-parser@^1.12.1", "@webassemblyjs/wasm-parser@1.12.1": +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": version "1.12.1" resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz" integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== @@ -5521,14 +4973,7 @@ abab@^2.0.3, abab@^2.0.5: resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - -accepts@^1.3.7, accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7, accepts@~1.3.8: +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -5559,16 +5004,16 @@ acorn-walk@^7.1.1: resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8, acorn@^8.2.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: - version "8.11.3" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" - integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== - acorn@^7.1.1: version "7.4.1" resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +acorn@^8.2.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.11.3" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + address@^1.0.1, address@^1.1.2: version "1.2.2" resolved "https://registry.npmjs.org/address/-/address-1.2.2.tgz" @@ -5608,7 +5053,7 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.9.1: +ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -5618,27 +5063,7 @@ ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.9.1: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0: - version "8.16.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz" - integrity sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== - dependencies: - fast-deep-equal "^3.1.3" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.4.1" - -ajv@^8.6.0, ajv@>=8: - version "8.16.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz" - integrity sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== - dependencies: - fast-deep-equal "^3.1.3" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.4.1" - -ajv@^8.8.2, ajv@^8.9.0: +ajv@^8.0.0, ajv@^8.6.0, ajv@^8.9.0: version "8.16.0" resolved "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz" integrity sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== @@ -5664,11 +5089,6 @@ amazon-quicksight-embedding-sdk@^1.18.1: resolved "https://registry.npmjs.org/amazon-quicksight-embedding-sdk/-/amazon-quicksight-embedding-sdk-1.20.1.tgz" integrity sha512-JYBCeq1Rq1OOeZQB9ry85uASTTtqZzp5hFv0ihIX7XyD9anvcoW6GHo4ttzdkJMTMMAnzXhIBe9CRKm3tHMzeg== -anser@^1.4.9: - version "1.4.10" - resolved "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz" - integrity sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww== - ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: version "4.3.2" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" @@ -5676,15 +5096,6 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: dependencies: type-fest "^0.21.3" -ansi-fragments@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/ansi-fragments/-/ansi-fragments-0.2.1.tgz" - integrity sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w== - dependencies: - colorette "^1.0.7" - slice-ansi "^2.0.0" - strip-ansi "^5.0.0" - ansi-html-community@^0.0.8: version "0.0.8" resolved "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz" @@ -5695,12 +5106,7 @@ ansi-html@^0.0.9: resolved "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.9.tgz" integrity sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg== -ansi-regex@^4.1.0: - version "4.1.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz" - integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== - -ansi-regex@^5.0.0, ansi-regex@^5.0.1: +ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -5710,7 +5116,7 @@ ansi-regex@^6.0.1: resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== -ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -5747,7 +5153,7 @@ anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -apexcharts@^3.33.2, apexcharts@^3.41.0: +apexcharts@^3.33.2: version "3.49.1" resolved "https://registry.npmjs.org/apexcharts/-/apexcharts-3.49.1.tgz" integrity sha512-MqGtlq/KQuO8j0BBsUJYlRG8VBctKwYdwuBtajHgHTmSgUU3Oai+8oYN/rKCXwXzrUlYA+GiMgotAIbXY2BCGw== @@ -5786,7 +5192,7 @@ apollo-cache-inmemory@^1.6.6: ts-invariant "^0.4.0" tslib "^1.10.0" -apollo-cache@^1.3.5, apollo-cache@1.3.5: +apollo-cache@1.3.5, apollo-cache@^1.3.5: version "1.3.5" resolved "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.5.tgz" integrity sha512-1XoDy8kJnyWY/i/+gLTEbYLnoiVtS8y7ikBr/IfmML4Qb+CM7dEEbIUOjnY716WqmZ/UpXIxTfJsY7rMcqiCXA== @@ -5845,7 +5251,7 @@ apollo-link@^1.0.0, apollo-link@^1.0.6, apollo-link@^1.2.14: tslib "^1.9.3" zen-observable-ts "^0.8.21" -apollo-utilities@^1.3.0, apollo-utilities@^1.3.4, apollo-utilities@1.3.4: +apollo-utilities@1.3.4, apollo-utilities@^1.3.0, apollo-utilities@^1.3.4: version "1.3.4" resolved "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz" integrity sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig== @@ -5864,11 +5270,6 @@ appbase-js@^5.3.4: querystring "^0.2.0" url-parser-lite "^0.1.0" -appdirsjs@^1.2.4: - version "1.2.7" - resolved "https://registry.npmjs.org/appdirsjs/-/appdirsjs-1.2.7.tgz" - integrity sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw== - arg@^5.0.2: version "5.0.2" resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" @@ -5886,13 +5287,6 @@ argparse@^2.0.1: resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^5.0.0, aria-query@^5.3.0, aria-query@5.3.0: - version "5.3.0" - resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz" - integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== - dependencies: - dequal "^2.0.3" - aria-query@5.1.3: version "5.1.3" resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz" @@ -5900,6 +5294,13 @@ aria-query@5.1.3: dependencies: deep-equal "^2.0.5" +aria-query@^5.0.0, aria-query@^5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz" @@ -6032,23 +5433,6 @@ ast-types-flow@^0.0.8: resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz" integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== -ast-types@0.15.2: - version "0.15.2" - resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz" - integrity sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg== - dependencies: - tslib "^2.0.1" - -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - async@^3.2.3: version "3.2.5" resolved "https://registry.npmjs.org/async/-/async-3.2.5.tgz" @@ -6128,11 +5512,6 @@ axobject-query@^3.2.1: dependencies: dequal "^2.0.3" -babel-core@^7.0.0-bridge.0: - version "7.0.0-bridge.0" - resolved "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz" - integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== - babel-jest@^27.4.2, babel-jest@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz" @@ -6246,13 +5625,6 @@ babel-plugin-syntax-jsx@^6.18.0: resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz" integrity sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw== -babel-plugin-transform-flow-enums@^0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz" - integrity sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ== - dependencies: - "@babel/plugin-syntax-flow" "^7.12.1" - babel-plugin-transform-react-remove-prop-types@^0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz" @@ -6316,7 +5688,7 @@ base-64@1.0.0: resolved "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz" integrity sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg== -base64-js@^1.0.2, base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -6347,15 +5719,6 @@ binary-extensions@^2.0.0: resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== -bl@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - bluebird@^3.7.2: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" @@ -6412,7 +5775,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.3, braces@~3.0.2, braces@3.0.3: +braces@3.0.3, braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== @@ -6424,7 +5787,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.21.4, browserslist@^4.22.2, browserslist@^4.23.0, "browserslist@>= 4", "browserslist@>= 4.21.0", browserslist@>=4: +browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.21.4, browserslist@^4.22.2, browserslist@^4.23.0: version "4.23.1" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz" integrity sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw== @@ -6446,22 +5809,6 @@ buffer-from@^1.0.0: resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^5.4.3: - version "5.7.1" - resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - buffer@4.9.2: version "4.9.2" resolved "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz" @@ -6471,6 +5818,14 @@ buffer@4.9.2: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.4.3: + version "5.7.1" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + builtin-modules@^3.1.0: version "3.3.0" resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz" @@ -6497,25 +5852,6 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz" - integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz" - integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz" - integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== - callsites@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" @@ -6543,17 +5879,12 @@ camelcase-keys@6.2.2: map-obj "^4.0.0" quick-lru "^4.0.1" -camelcase@^5.0.0, camelcase@^5.3.1: +camelcase@^5.3.1: version "5.3.1" resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.0: - version "6.3.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -camelcase@^6.2.1: +camelcase@^6.2.0, camelcase@^6.2.1: version "6.3.0" resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -6600,31 +5931,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.0.2: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -6662,26 +5969,11 @@ chokidar@^3.4.2, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -chrome-launcher@^0.15.2: - version "0.15.2" - resolved "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz" - integrity sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ== - dependencies: - "@types/node" "*" - escape-string-regexp "^4.0.0" - is-wsl "^2.2.0" - lighthouse-logger "^1.0.0" - chrome-trace-event@^1.0.2: version "1.0.4" resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz" integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== -ci-info@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== - ci-info@^3.2.0: version "3.9.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" @@ -6704,27 +5996,6 @@ clean-css@^5.2.2: dependencies: source-map "~0.6.0" -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-spinners@^2.5.0: - version "2.9.2" - resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz" - integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - cliui@^7.0.2: version "7.0.4" resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" @@ -6734,35 +6005,7 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" - integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== - -clsx@^1.1.0: - version "1.2.1" - resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" - integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== - -clsx@^1.2.1: +clsx@^1.1.0, clsx@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== @@ -6805,26 +6048,21 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + colord@^2.9.1: version "2.9.3" resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz" integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== -colorette@^1.0.7: - version "1.4.0" - resolved "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz" - integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== - colorette@^2.0.10: version "2.0.20" resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" @@ -6837,17 +6075,7 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -command-exists@^1.2.8: - version "1.2.9" - resolved "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz" - integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== - -commander@^2.20.0: - version "2.20.3" - resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commander@^2.20.3: +commander@^2.20.0, commander@^2.20.3: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -6867,11 +6095,6 @@ commander@^8.3.0: resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== -commander@^9.4.1: - version "9.5.0" - resolved "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz" - integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== - common-tags@^1.8.0: version "1.8.2" resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz" @@ -6889,7 +6112,7 @@ compressible@~2.0.16: dependencies: mime-db ">= 1.43.0 < 2" -compression@^1.7.1, compression@^1.7.4: +compression@^1.7.4: version "1.7.4" resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz" integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== @@ -6917,16 +6140,6 @@ connect-history-api-fallback@^2.0.0: resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz" integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== -connect@^3.6.5: - version "3.7.0" - resolved "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz" - integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== - dependencies: - debug "2.6.9" - finalhandler "1.1.2" - parseurl "~1.3.3" - utils-merge "1.0.1" - content-disposition@0.5.4: version "0.5.4" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" @@ -6954,16 +6167,16 @@ cookie-signature@1.0.6: resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@^0.4.0: - version "0.4.2" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== - cookie@0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz" integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== +cookie@^0.4.0: + version "0.4.2" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + copy-to-clipboard@^3.3.1: version "3.3.3" resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz" @@ -6993,26 +6206,6 @@ core-util-is@~1.0.0: resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cosmiconfig@^5.0.5: - version "5.2.1" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - -cosmiconfig@^5.1.0: - version "5.2.1" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - cosmiconfig@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz" @@ -7060,25 +6253,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0: - version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -7173,15 +6348,15 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" -css-tree@^1.1.2: - version "1.1.3" - resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== dependencies: - mdn-data "2.0.14" + mdn-data "2.0.4" source-map "^0.6.1" -css-tree@^1.1.3: +css-tree@^1.1.2, css-tree@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== @@ -7189,14 +6364,6 @@ css-tree@^1.1.3: mdn-data "2.0.14" source-map "^0.6.1" -css-tree@1.0.0-alpha.37: - version "1.0.0-alpha.37" - resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz" - integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== - dependencies: - mdn-data "2.0.4" - source-map "^0.6.1" - css-vendor@^2.0.8: version "2.0.8" resolved "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz" @@ -7313,12 +6480,7 @@ csstype@^2.5.7: resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz" integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w== -csstype@^3.0.2: - version "3.1.3" - resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== - -csstype@^3.1.3: +csstype@^3.0.2, csstype@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== @@ -7364,38 +6526,31 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" -date-fns@^2.0.0, date-fns@^2.25.0, date-fns@^2.28.0: +date-fns@^2.28.0: version "2.30.0" resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== dependencies: "@babel/runtime" "^7.21.0" -dayjs@^1.10.7, dayjs@^1.11.0, dayjs@^1.11.7, dayjs@^1.8.15, dayjs@^1.8.17: +dayjs@^1.11.0, dayjs@^1.11.7: version "1.11.11" resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz" integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg== -debug@^2.2.0: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^2.6.0: +debug@2.6.9, debug@^2.6.0: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.5" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== dependencies: - ms "2.0.0" + ms "2.1.2" debug@^3.2.7: version "3.2.7" @@ -7404,25 +6559,6 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@4: - version "4.3.5" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz" - integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== - dependencies: - ms "2.1.2" - -debug@2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" - integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== - decimal.js@^10.2.1: version "10.4.3" resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz" @@ -7472,11 +6608,6 @@ deepmerge@^4.2.2: resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== -deepmerge@^4.3.0: - version "4.3.1" - resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" - integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== - default-gateway@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz" @@ -7484,13 +6615,6 @@ default-gateway@^6.0.3: dependencies: execa "^5.0.0" -defaults@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz" - integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== - dependencies: - clone "^1.0.2" - define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" @@ -7519,21 +6643,16 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -denodeify@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz" - integrity sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg== +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== -depd@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - dequal@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" @@ -7630,6 +6749,14 @@ dom-helpers@^5.0.1: "@babel/runtime" "^7.8.7" csstype "^3.0.2" +dom-serializer@0: + version "0.2.2" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz" @@ -7639,24 +6766,16 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" -dom-serializer@0: - version "0.2.2" - resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" +domelementtype@1: + version "1.3.1" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== -domelementtype@1: - version "1.3.1" - resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - domexception@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz" @@ -7734,7 +6853,7 @@ echarts-for-react@^3.0.2: fast-deep-equal "^3.1.3" size-sensor "^1.0.1" -"echarts@^3.0.0 || ^4.0.0 || ^5.0.0", echarts@^5.3.3: +echarts@^5.3.3: version "5.5.0" resolved "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz" integrity sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw== @@ -7747,7 +6866,7 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -ejs@3.1.10: +ejs@3.1.10, ejs@^3.1.6: version "3.1.10" resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz" integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== @@ -7806,7 +6925,7 @@ enhanced-resolve@^5.16.0: graceful-fs "^4.2.4" tapable "^2.2.0" -entities@^2.0.0, entities@2.2.0: +entities@2.2.0, entities@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== @@ -7819,11 +6938,6 @@ env-cmd@^10.1.0: commander "^4.0.0" cross-spawn "^7.0.0" -envinfo@^7.10.0: - version "7.13.0" - resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz" - integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q== - error-ex@^1.3.1: version "1.3.2" resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" @@ -7838,14 +6952,6 @@ error-stack-parser@^2.0.6: dependencies: stackframe "^1.3.4" -errorhandler@^1.5.1: - version "1.5.1" - resolved "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz" - integrity sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A== - dependencies: - accepts "~1.3.7" - escape-html "~1.0.3" - es-abstract@^1.17.2, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: version "1.23.3" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz" @@ -8089,7 +7195,7 @@ eslint-plugin-flowtype@^8.0.3: lodash "^4.17.21" string-natural-compare "^3.0.1" -eslint-plugin-import@^2.25.3, eslint-plugin-import@^2.27.5, eslint-plugin-import@>=1.4.0: +eslint-plugin-import@^2.25.3, eslint-plugin-import@^2.27.5: version "2.29.1" resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz" integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== @@ -8184,7 +7290,7 @@ eslint-plugin-testing-library@^5.0.1: dependencies: "@typescript-eslint/utils" "^5.58.0" -eslint-scope@^5.1.1: +eslint-scope@5.1.1, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -8200,14 +7306,6 @@ eslint-scope@^7.2.2: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-scope@5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - eslint-visitor-keys@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" @@ -8229,7 +7327,7 @@ eslint-webpack-plugin@^3.1.1: normalize-path "^3.0.0" schema-utils "^4.0.0" -eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", "eslint@^7.5.0 || ^8.0.0", "eslint@^7.5.0 || ^8.0.0 || ^9.0.0", eslint@^8.0.0, eslint@^8.1.0, eslint@^8.3.0, "eslint@>= 6", eslint@>=7.0.0, eslint@>=7.28.0: +eslint@^8.3.0: version "8.57.0" resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz" integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== @@ -8282,16 +7380,16 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - esprima@1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz" integrity sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A== +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + esquery@^1.4.2: version "1.5.0" resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz" @@ -8306,12 +7404,7 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^4.2.0: +estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -8336,11 +7429,6 @@ etag@~1.8.1: resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -event-target-shim@^5.0.0, event-target-shim@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" @@ -8358,7 +7446,7 @@ exec-sh@^0.2.0: dependencies: merge "^1.2.0" -execa@^5.0.0, execa@^5.1.1: +execa@^5.0.0: version "5.1.1" resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -8399,7 +7487,7 @@ expect@^29.0.0: jest-message-util "^29.7.0" jest-util "^29.7.0" -express@4.19.2: +express@4.19.2, express@^4.17.3: version "4.19.2" resolved "https://registry.npmjs.org/express/-/express-4.19.2.tgz" integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== @@ -8451,7 +7539,7 @@ fast-diff@^1.1.2: resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.2: +fast-glob@^3.2.9, fast-glob@^3.3.0: version "3.3.2" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -8472,17 +7560,10 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fast-xml-parser@^4.0.12, fast-xml-parser@^4.2.4, fast-xml-parser@^4.2.5: - version "4.4.0" - resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.0.tgz" - integrity sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg== - dependencies: - strnum "^1.0.5" - -fast-xml-parser@4.2.5: - version "4.2.5" - resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz" - integrity sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g== +fast-xml-parser@4.2.5, fast-xml-parser@4.4.1, fast-xml-parser@^4.2.5: + version "4.4.1" + resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz" + integrity sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw== dependencies: strnum "^1.0.5" @@ -8553,19 +7634,6 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - finalhandler@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" @@ -8579,15 +7647,6 @@ finalhandler@1.2.0: statuses "2.0.1" unpipe "~1.0.0" -find-cache-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - find-cache-dir@^3.3.1: version "3.3.2" resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" @@ -8609,15 +7668,7 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-up@^4.1.0: +find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -8647,17 +7698,7 @@ flatted@^3.2.9: resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== -flow-enums-runtime@^0.0.6: - version "0.0.6" - resolved "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz" - integrity sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw== - -flow-parser@0.*: - version "0.237.2" - resolved "https://registry.npmjs.org/flow-parser/-/flow-parser-0.237.2.tgz" - integrity sha512-mvI/kdfr3l1waaPbThPA8dJa77nHXrfZIun+SWvFwSwDjmeByU7mGJGRmv1+7guU6ccyLV8e1lqZA1lD4iMGnQ== - -follow-redirects@1.15.6: +follow-redirects@1.15.6, follow-redirects@^1.0.0, follow-redirects@^1.15.6: version "1.15.6" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== @@ -8752,26 +7793,7 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^9.0.0: - version "9.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-extra@^9.0.1: +fs-extra@^9.0.0, fs-extra@^9.0.1: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -8821,7 +7843,7 @@ gensync@^1.0.0-beta.2: resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.1, get-caller-file@^2.0.5: +get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -8861,7 +7883,7 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" -glob-parent@^5.1.2: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -8875,13 +7897,6 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" @@ -8965,7 +7980,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -8982,7 +7997,7 @@ graphql-tag@^2.12.6, graphql-tag@^2.4.2: dependencies: tslib "^2.1.0" -"graphql@^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0", "graphql@^0.11.3 || ^0.12.3 || ^0.13.0 || ^14.0.0 || ^15.0.0", "graphql@^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", "graphql@^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^15.0.0 || ^16.0.0", graphql@15.8.0: +graphql@15.8.0: version "15.8.0" resolved "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz" integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw== @@ -9055,37 +8070,6 @@ he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -hermes-estree@0.19.1: - version "0.19.1" - resolved "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.19.1.tgz" - integrity sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g== - -hermes-estree@0.20.1: - version "0.20.1" - resolved "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.20.1.tgz" - integrity sha512-SQpZK4BzR48kuOg0v4pb3EAGNclzIlqMj3Opu/mu7bbAoFw6oig6cEt/RAi0zTFW/iW6Iz9X9ggGuZTAZ/yZHg== - -hermes-parser@0.19.1: - version "0.19.1" - resolved "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.19.1.tgz" - integrity sha512-Vp+bXzxYJWrpEuJ/vXxUsLnt0+y4q9zyi4zUlkLqD8FKv4LjIfOvP69R/9Lty3dCyKh0E2BU7Eypqr63/rKT/A== - dependencies: - hermes-estree "0.19.1" - -hermes-parser@0.20.1: - version "0.20.1" - resolved "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.20.1.tgz" - integrity sha512-BL5P83cwCogI8D7rrDCgsFY0tdYUtmFP9XaXtl2IQjC+2Xo+4okjfXintlTxcIwl4qeGddEl28Z11kbVIw0aNA== - dependencies: - hermes-estree "0.20.1" - -hermes-profile-transformer@^0.0.6: - version "0.0.6" - resolved "https://registry.npmjs.org/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz" - integrity sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ== - dependencies: - source-map "^0.7.3" - history@^5.0.3: version "5.3.0" resolved "https://registry.npmjs.org/history/-/history-5.3.0.tgz" @@ -9176,16 +8160,6 @@ http-deceiver@^1.2.7: resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - http-errors@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" @@ -9197,6 +8171,16 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + http-parser-js@>=0.5.1: version "0.5.8" resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" @@ -9249,13 +8233,6 @@ hyphenate-style-name@^1.0.3: resolved "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.5.tgz" integrity sha512-fedL7PRwmeVkgyhu9hLeTBaI6wcGk7JGJswdaRsa5aUbkXI1kr1xZwTPBtaYPpwf56878iDek6VbVnuWMebJmw== -iconv-lite@^0.6.3: - version "0.6.3" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" @@ -9263,21 +8240,28 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -idb@^7.0.1: - version "7.1.1" - resolved "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz" - integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== - idb@5.0.6: version "5.0.6" resolved "https://registry.npmjs.org/idb/-/idb-5.0.6.tgz" integrity sha512-/PFvOWPzRcEPmlDt5jEvzVZVs0wyd/EvGvkDIcbBpGuMMLQKrTPG0TxvE2UJtgZtCQCmOtM2QD7yQJBVEjKGOw== +idb@^7.0.1: + version "7.1.1" + resolved "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz" + integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== + identity-obj-proxy@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz" @@ -9295,30 +8279,15 @@ ignore@^5.2.0: resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== -image-size@^1.0.2: - version "1.1.1" - resolved "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz" - integrity sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ== - dependencies: - queue "6.0.2" - -immer@^9.0.21, immer@^9.0.7: - version "9.0.21" - resolved "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz" - integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== - immer@9.0.6: version "9.0.6" resolved "https://registry.npmjs.org/immer/-/immer-9.0.6.tgz" integrity sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ== -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz" - integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" +immer@^9.0.21, immer@^9.0.7: + version "9.0.21" + resolved "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz" + integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" @@ -9354,7 +8323,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@2, inherits@2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -9385,16 +8354,21 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -ipaddr.js@^2.0.1: - version "2.2.0" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== +ip@1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.9.tgz#8dfbcc99a754d07f425310b86a99546b1151e396" + integrity sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ== ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +ipaddr.js@^2.0.1: + version "2.2.0" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" @@ -9471,11 +8445,6 @@ is-date-object@^1.0.1, is-date-object@^1.0.5: dependencies: has-tostringtag "^1.0.0" -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz" - integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== - is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" @@ -9493,11 +8462,6 @@ is-finalizationregistry@^1.0.2: dependencies: call-bind "^1.0.2" -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" - integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" @@ -9527,11 +8491,6 @@ is-in-browser@^1.0.2, is-in-browser@^1.1.3: resolved "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz" integrity sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g== -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - is-map@^2.0.2, is-map@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz" @@ -9574,13 +8533,6 @@ is-plain-obj@^3.0.0: resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz" integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" @@ -9647,11 +8599,6 @@ is-typedarray@^1.0.0: resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - is-weakmap@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz" @@ -9672,12 +8619,7 @@ is-weakset@^2.0.3: call-bind "^1.0.7" get-intrinsic "^1.2.4" -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz" - integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== - -is-wsl@^2.1.1, is-wsl@^2.2.0: +is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== @@ -9699,11 +8641,6 @@ isexe@^2.0.0: resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - isomorphic-unfetch@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz" @@ -9929,18 +8866,6 @@ jest-environment-node@^27.5.1: jest-mock "^27.5.1" jest-util "^27.5.1" -jest-environment-node@^29.6.3: - version "29.7.0" - resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz" - integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - jest-util "^29.7.0" - jest-get-type@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz" @@ -10075,15 +9000,6 @@ jest-mock@^27.5.1: "@jest/types" "^27.5.1" "@types/node" "*" -jest-mock@^29.7.0: - version "29.7.0" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz" - integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-util "^29.7.0" - jest-pnp-resolver@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz" @@ -10108,7 +9024,7 @@ jest-resolve-dependencies@^27.5.1: jest-regex-util "^27.5.1" jest-snapshot "^27.5.1" -jest-resolve@*, jest-resolve@^27.4.2, jest-resolve@^27.5.1: +jest-resolve@^27.4.2, jest-resolve@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz" integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== @@ -10263,18 +9179,6 @@ jest-validate@^27.5.1: leven "^3.1.0" pretty-format "^27.5.1" -jest-validate@^29.6.3: - version "29.7.0" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz" - integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== - dependencies: - "@jest/types" "^29.6.3" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^29.6.3" - leven "^3.1.0" - pretty-format "^29.7.0" - jest-watch-typeahead@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz" @@ -10342,17 +9246,7 @@ jest-worker@^28.0.2: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.6.3: - version "29.7.0" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz" - integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== - dependencies: - "@types/node" "*" - jest-util "^29.7.0" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -"jest@^27.0.0 || ^28.0.0", jest@^27.4.3: +jest@^27.4.3: version "27.5.1" resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz" integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== @@ -10366,17 +9260,6 @@ jiti@^1.21.0: resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz" integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== -joi@^17.2.1: - version "17.13.1" - resolved "https://registry.npmjs.org/joi/-/joi-17.13.1.tgz" - integrity sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg== - dependencies: - "@hapi/hoek" "^9.3.0" - "@hapi/topo" "^5.1.0" - "@sideway/address" "^4.1.5" - "@sideway/formula" "^3.0.1" - "@sideway/pinpoint" "^2.0.0" - js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz" @@ -10402,41 +9285,6 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsc-android@^250231.0.0: - version "250231.0.0" - resolved "https://registry.npmjs.org/jsc-android/-/jsc-android-250231.0.0.tgz" - integrity sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw== - -jsc-safe-url@^0.2.2: - version "0.2.4" - resolved "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz" - integrity sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q== - -jscodeshift@^0.14.0: - version "0.14.0" - resolved "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.14.0.tgz" - integrity sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA== - dependencies: - "@babel/core" "^7.13.16" - "@babel/parser" "^7.13.16" - "@babel/plugin-proposal-class-properties" "^7.13.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8" - "@babel/plugin-proposal-optional-chaining" "^7.13.12" - "@babel/plugin-transform-modules-commonjs" "^7.13.8" - "@babel/preset-flow" "^7.13.13" - "@babel/preset-typescript" "^7.13.0" - "@babel/register" "^7.13.16" - babel-core "^7.0.0-bridge.0" - chalk "^4.1.2" - flow-parser "0.*" - graceful-fs "^4.2.4" - micromatch "^4.0.4" - neo-async "^2.5.0" - node-dir "^0.1.17" - recast "^0.21.0" - temp "^0.8.4" - write-file-atomic "^2.3.0" - jsdom@^16.6.0: version "16.7.0" resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz" @@ -10485,11 +9333,6 @@ json-buffer@3.0.1: resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" @@ -10527,13 +9370,6 @@ json5@^2.1.2, json5@^2.2.0, json5@^2.2.2, json5@^2.2.3: resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz" - integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== - optionalDependencies: - graceful-fs "^4.1.6" - jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" @@ -10617,7 +9453,7 @@ jss-plugin-vendor-prefixer@^10.10.0: css-vendor "^2.0.8" jss "10.10.0" -jss@^10.10.0, jss@10.10.0: +jss@10.10.0, jss@^10.10.0: version "10.10.0" resolved "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz" integrity sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw== @@ -10705,14 +9541,6 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -lighthouse-logger@^1.0.0: - version "1.4.2" - resolved "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz" - integrity sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g== - dependencies: - debug "^2.6.9" - marky "^1.2.2" - lilconfig@^2.0.3, lilconfig@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz" @@ -10809,23 +9637,6 @@ lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -logkitty@^0.7.1: - version "0.7.1" - resolved "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz" - integrity sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ== - dependencies: - ansi-fragments "^0.2.1" - dayjs "^1.8.15" - yargs "^15.1.0" - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" @@ -10864,14 +9675,6 @@ magic-string@^0.25.0, magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.8" -make-dir@^2.0.0, make-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - make-dir@^3.0.2, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" @@ -10898,11 +9701,6 @@ map-obj@^4.0.0: resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== -marky@^1.2.2: - version "1.2.5" - resolved "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz" - integrity sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q== - mdn-data@2.0.14: version "2.0.14" resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz" @@ -10925,11 +9723,6 @@ memfs@^3.1.2, memfs@^3.4.3: dependencies: fs-monkey "^1.0.4" -memoize-one@^5.0.0: - version "5.2.1" - resolved "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz" - integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" @@ -10940,205 +9733,21 @@ merge-stream@^2.0.0: resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge@2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz" - integrity sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w== - merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +merge@2.1.1, merge@^1.2.0: + version "2.1.1" + resolved "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz" + integrity sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w== + methods@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -metro-babel-transformer@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.80.9.tgz" - integrity sha512-d76BSm64KZam1nifRZlNJmtwIgAeZhZG3fi3K+EmPOlrR8rDtBxQHDSN3fSGeNB9CirdTyabTMQCkCup6BXFSQ== - dependencies: - "@babel/core" "^7.20.0" - hermes-parser "0.20.1" - nullthrows "^1.1.1" - -metro-cache-key@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.80.9.tgz" - integrity sha512-hRcYGhEiWIdM87hU0fBlcGr+tHDEAT+7LYNCW89p5JhErFt/QaAkVx4fb5bW3YtXGv5BTV7AspWPERoIb99CXg== - -metro-cache@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-cache/-/metro-cache-0.80.9.tgz" - integrity sha512-ujEdSI43QwI+Dj2xuNax8LMo8UgKuXJEdxJkzGPU6iIx42nYa1byQ+aADv/iPh5sh5a//h5FopraW5voXSgm2w== - dependencies: - metro-core "0.80.9" - rimraf "^3.0.2" - -metro-config@^0.80.3, metro-config@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-config/-/metro-config-0.80.9.tgz" - integrity sha512-28wW7CqS3eJrunRGnsibWldqgwRP9ywBEf7kg+uzUHkSFJNKPM1K3UNSngHmH0EZjomizqQA2Zi6/y6VdZMolg== - dependencies: - connect "^3.6.5" - cosmiconfig "^5.0.5" - jest-validate "^29.6.3" - metro "0.80.9" - metro-cache "0.80.9" - metro-core "0.80.9" - metro-runtime "0.80.9" - -metro-core@^0.80.3, metro-core@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-core/-/metro-core-0.80.9.tgz" - integrity sha512-tbltWQn+XTdULkGdzHIxlxk4SdnKxttvQQV3wpqqFbHDteR4gwCyTR2RyYJvxgU7HELfHtrVbqgqAdlPByUSbg== - dependencies: - lodash.throttle "^4.1.1" - metro-resolver "0.80.9" - -metro-file-map@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.80.9.tgz" - integrity sha512-sBUjVtQMHagItJH/wGU9sn3k2u0nrCl0CdR4SFMO1tksXLKbkigyQx4cbpcyPVOAmGTVuy3jyvBlELaGCAhplQ== - dependencies: - anymatch "^3.0.3" - debug "^2.2.0" - fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - invariant "^2.2.4" - jest-worker "^29.6.3" - micromatch "^4.0.4" - node-abort-controller "^3.1.1" - nullthrows "^1.1.1" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.3.2" - -metro-minify-terser@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.80.9.tgz" - integrity sha512-FEeCeFbkvvPuhjixZ1FYrXtO0araTpV6UbcnGgDUpH7s7eR5FG/PiJz3TsuuPP/HwCK19cZtQydcA2QrCw446A== - dependencies: - terser "^5.15.0" - -metro-resolver@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.80.9.tgz" - integrity sha512-wAPIjkN59BQN6gocVsAvvpZ1+LQkkqUaswlT++cJafE/e54GoVkMNCmrR4BsgQHr9DknZ5Um/nKueeN7kaEz9w== - -metro-runtime@^0.80.3, metro-runtime@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.80.9.tgz" - integrity sha512-8PTVIgrVcyU+X/rVCy/9yxNlvXsBCk5JwwkbAm/Dm+Abo6NBGtNjWF0M1Xo/NWCb4phamNWcD7cHdR91HhbJvg== - dependencies: - "@babel/runtime" "^7.0.0" - -metro-source-map@^0.80.3, metro-source-map@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.80.9.tgz" - integrity sha512-RMn+XS4VTJIwMPOUSj61xlxgBvPeY4G6s5uIn6kt6HB6A/k9ekhr65UkkDD7WzHYs3a9o869qU8tvOZvqeQzgw== - dependencies: - "@babel/traverse" "^7.20.0" - "@babel/types" "^7.20.0" - invariant "^2.2.4" - metro-symbolicate "0.80.9" - nullthrows "^1.1.1" - ob1 "0.80.9" - source-map "^0.5.6" - vlq "^1.0.0" - -metro-symbolicate@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.80.9.tgz" - integrity sha512-Ykae12rdqSs98hg41RKEToojuIW85wNdmSe/eHUgMkzbvCFNVgcC0w3dKZEhSsqQOXapXRlLtHkaHLil0UD/EA== - dependencies: - invariant "^2.2.4" - metro-source-map "0.80.9" - nullthrows "^1.1.1" - source-map "^0.5.6" - through2 "^2.0.1" - vlq "^1.0.0" - -metro-transform-plugins@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.80.9.tgz" - integrity sha512-UlDk/uc8UdfLNJhPbF3tvwajyuuygBcyp+yBuS/q0z3QSuN/EbLllY3rK8OTD9n4h00qZ/qgxGv/lMFJkwP4vg== - dependencies: - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.0" - "@babel/template" "^7.0.0" - "@babel/traverse" "^7.20.0" - nullthrows "^1.1.1" - -metro-transform-worker@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.80.9.tgz" - integrity sha512-c/IrzMUVnI0hSVVit4TXzt3A1GiUltGVlzCmLJWxNrBGHGrJhvgePj38+GXl1Xf4Fd4vx6qLUkKMQ3ux73bFLQ== - dependencies: - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.0" - "@babel/parser" "^7.20.0" - "@babel/types" "^7.20.0" - metro "0.80.9" - metro-babel-transformer "0.80.9" - metro-cache "0.80.9" - metro-cache-key "0.80.9" - metro-minify-terser "0.80.9" - metro-source-map "0.80.9" - metro-transform-plugins "0.80.9" - nullthrows "^1.1.1" - -metro@^0.80.3, metro@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/metro/-/metro-0.80.9.tgz" - integrity sha512-Bc57Xf3GO2Xe4UWQsBj/oW6YfLPABEu8jfDVDiNmJvoQW4CO34oDPuYKe4KlXzXhcuNsqOtSxpbjCRRVjhhREg== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.0" - "@babel/parser" "^7.20.0" - "@babel/template" "^7.0.0" - "@babel/traverse" "^7.20.0" - "@babel/types" "^7.20.0" - accepts "^1.3.7" - chalk "^4.0.0" - ci-info "^2.0.0" - connect "^3.6.5" - debug "^2.2.0" - denodeify "^1.2.1" - error-stack-parser "^2.0.6" - graceful-fs "^4.2.4" - hermes-parser "0.20.1" - image-size "^1.0.2" - invariant "^2.2.4" - jest-worker "^29.6.3" - jsc-safe-url "^0.2.2" - lodash.throttle "^4.1.1" - metro-babel-transformer "0.80.9" - metro-cache "0.80.9" - metro-cache-key "0.80.9" - metro-config "0.80.9" - metro-core "0.80.9" - metro-file-map "0.80.9" - metro-resolver "0.80.9" - metro-runtime "0.80.9" - metro-source-map "0.80.9" - metro-symbolicate "0.80.9" - metro-transform-plugins "0.80.9" - metro-transform-worker "0.80.9" - mime-types "^2.1.27" - node-fetch "^2.2.0" - nullthrows "^1.1.1" - rimraf "^3.0.2" - serialize-error "^2.1.0" - source-map "^0.5.6" - strip-ansi "^6.0.0" - throat "^5.0.0" - ws "^7.5.1" - yargs "^17.6.2" - micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.7" resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz" @@ -11147,7 +9756,7 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.3" picomatch "^2.3.1" -"mime-db@>= 1.43.0 < 2", mime-db@1.52.0: +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== @@ -11159,11 +9768,6 @@ mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, dependencies: mime-db "1.52.0" -mime@^2.4.1: - version "2.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - mime@1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" @@ -11192,7 +9796,7 @@ minimalistic-assert@^1.0.0: resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -11223,33 +9827,23 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@~0.5.1: version "0.5.6" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== dependencies: minimist "^1.2.6" -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -"monaco-editor@>= 0.21.0 < 1", "monaco-editor@>= 0.25.0 < 1": - version "0.49.0" - resolved "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.49.0.tgz" - integrity sha512-2I8/T3X/hLxB2oPHgqcNYUVdA/ZEFShT7IAujifIPMfKkNbLOqY8XCoyHCXrsdjb36dW9MwoTwBCFpXKMwNwaQ== - -ms@^2.1.1, ms@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== +ms@2.1.2, ms@^2.1.1: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + ms@2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" @@ -11297,7 +9891,7 @@ negotiator@0.6.3: resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -neo-async@^2.5.0, neo-async@^2.6.2: +neo-async@^2.6.2: version "2.6.2" resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== @@ -11315,24 +9909,7 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -nocache@^3.0.1: - version "3.0.4" - resolved "https://registry.npmjs.org/nocache/-/nocache-3.0.4.tgz" - integrity sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw== - -node-abort-controller@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz" - integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== - -node-dir@^0.1.17: - version "0.1.17" - resolved "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz" - integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== - dependencies: - minimatch "^3.0.2" - -node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.12: +node-fetch@^2.6.1, node-fetch@^2.6.12: version "2.7.0" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -11354,11 +9931,6 @@ node-releases@^2.0.14: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== -node-stream-zip@^1.9.1: - version "1.15.0" - resolved "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz" - integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== - normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" @@ -11394,28 +9966,18 @@ nprogress@^0.2.0: resolved "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz" integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA== -nth-check@^2.0.1: +nth-check@^1.0.2, nth-check@^2.0.1: version "2.1.1" resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== dependencies: boolbase "^1.0.0" -nullthrows@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz" - integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== - nwsapi@^2.2.0: version "2.2.10" resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz" integrity sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ== -ob1@0.80.9: - version "0.80.9" - resolved "https://registry.npmjs.org/ob1/-/ob1-0.80.9.tgz" - integrity sha512-v9yOxowkZbxWhKOaaTyLjIm1aLy4ebMNcSn4NYJKOAI/Qv+SkfEfszpLr2GIxsccmb2Y2HA9qtsqiIJ80ucpVA== - object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" @@ -11518,7 +10080,7 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -oidc-client-ts@^2.2.1, oidc-client-ts@^2.4.0: +oidc-client-ts@^2.4.0: version "2.4.0" resolved "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-2.4.0.tgz" integrity sha512-WijhkTrlXK2VvgGoakWJiBdfIsVGz6CFzgjNNqZU1hPKV2kyeEaJgLs7RwuiSp2WhLfWBQuLvr2SxVlZnk3N1w== @@ -11526,13 +10088,6 @@ oidc-client-ts@^2.2.1, oidc-client-ts@^2.4.0: crypto-js "^4.2.0" jwt-decode "^3.1.2" -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" - integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== - dependencies: - ee-first "1.1.1" - on-finished@2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" @@ -11552,28 +10107,13 @@ once@^1.3.0: dependencies: wrappy "1" -onetime@^5.1.0, onetime@^5.1.2: +onetime@^5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" -open@^6.2.0: - version "6.4.0" - resolved "https://registry.npmjs.org/open/-/open-6.4.0.tgz" - integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== - dependencies: - is-wsl "^1.1.0" - -open@^7.0.3: - version "7.4.2" - resolved "https://registry.npmjs.org/open/-/open-7.4.2.tgz" - integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== - dependencies: - is-docker "^2.0.0" - is-wsl "^2.1.1" - open@^8.0.9, open@^8.4.0: version "8.4.2" resolved "https://registry.npmjs.org/open/-/open-8.4.2.tgz" @@ -11624,29 +10164,7 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" -ora@^5.4.1: - version "5.4.1" - resolved "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz" - integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" - wcwidth "^1.0.1" - -p-limit@^2.0.0: - version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^2.2.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -11714,14 +10232,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" - integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" @@ -11770,12 +10280,7 @@ path-key@^2.0.1: resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== -path-key@^3.0.0: - version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -11823,23 +10328,11 @@ pify@^2.3.0: resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pirates@^4.0.1, pirates@^4.0.4, pirates@^4.0.6: +pirates@^4.0.1, pirates@^4.0.4: version "4.0.6" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" @@ -12399,7 +10892,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^ resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@8.4.31: +postcss@8.4.31, postcss@^8.2.14, postcss@^8.3.5, postcss@^8.4.23, postcss@^8.4.33, postcss@^8.4.4: version "8.4.31" resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz" integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== @@ -12425,7 +10918,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@>=2.0.0, prettier@2.8.7: +prettier@2.8.7: version "2.8.7" resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz" integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== @@ -12443,26 +10936,6 @@ pretty-error@^4.0.0: lodash "^4.17.20" renderkid "^3.0.0" -pretty-format@^26.5.2: - version "26.6.2" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz" - integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== - dependencies: - "@jest/types" "^26.6.2" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^17.0.1" - -pretty-format@^26.6.2: - version "26.6.2" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz" - integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== - dependencies: - "@jest/types" "^26.6.2" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^17.0.1" - pretty-format@^27.0.2, pretty-format@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz" @@ -12482,16 +10955,7 @@ pretty-format@^28.1.3: ansi-styles "^5.0.0" react-is "^18.0.0" -pretty-format@^29.0.0: - version "29.7.0" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz" - integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== - dependencies: - "@jest/schemas" "^29.6.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -pretty-format@^29.7.0: +pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz" integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== @@ -12505,7 +10969,7 @@ process-nextick-args@~2.0.0: resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -promise@^8.1.0, promise@^8.3.0: +promise@^8.1.0: version "8.3.0" resolved "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz" integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== @@ -12520,7 +10984,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1, prop-types@>=15: +prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -12552,16 +11016,16 @@ psl@^1.1.33: resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== -punycode@^2.1.0, punycode@^2.1.1: - version "2.3.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - punycode@1.3.2: version "1.3.2" resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== +punycode@^2.1.0, punycode@^2.1.1: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + q@^1.1.2: version "1.5.1" resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz" @@ -12574,16 +11038,16 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" -querystring@^0.2.0, querystring@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz" - integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== - querystring@0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== +querystring@^0.2.0: + version "0.2.1" + resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz" + integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" @@ -12594,13 +11058,6 @@ queue-microtask@^1.2.2: resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -queue@6.0.2: - version "6.0.2" - resolved "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz" - integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== - dependencies: - inherits "~2.0.3" - quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz" @@ -12699,15 +11156,7 @@ react-dev-utils@^12.0.1: strip-ansi "^6.0.1" text-table "^0.2.0" -react-devtools-core@^5.0.0: - version "5.2.0" - resolved "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-5.2.0.tgz" - integrity sha512-vZK+/gvxxsieAoAyYaiRIVFxlajb7KXhgBDV7OsoMzaAE+IqGpoxusBjIgq5ibqA2IloKu0p9n7tE68z1xs18A== - dependencies: - shell-quote "^1.6.1" - ws "^7" - -"react-dom@^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0 || ^17.0 || ^18.0.0", "react-dom@^16.6.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^17.0.2, "react-dom@^17.0.2 || ^18.0.0", react-dom@<18.0.0, react-dom@>=15, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0: +react-dom@^17.0.2: version "17.0.2" resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== @@ -12761,30 +11210,20 @@ react-if@^4.1.1: resolved "https://registry.npmjs.org/react-if/-/react-if-4.1.5.tgz" integrity sha512-Uk+Ub2gC83PAakuU4+7iLdTEP4LPi2ihNEPCtz/vr8SLGbzkMApbpYbkDZ5z9zYXurd0gg+EK/bpOLFFC1r1eQ== -"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0: - version "18.3.1" - resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz" - integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== - -react-is@^16.13.1: - version "16.13.1" - resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-is@^16.7.0: +react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^17.0.1: +react-is@^17.0.1, react-is@^17.0.2: version "17.0.2" resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^17.0.2: - version "17.0.2" - resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0, react-is@^18.2.0: + version "18.3.1" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== react-native-get-random-values@^1.4.0: version "1.11.0" @@ -12800,55 +11239,12 @@ react-native-url-polyfill@^1.3.0: dependencies: whatwg-url-without-unicode "8.0.0-3" -react-native@*, react-native@>=0.56: - version "0.74.2" - resolved "https://registry.npmjs.org/react-native/-/react-native-0.74.2.tgz" - integrity sha512-EBMBjPPL4/GjHMP4NqsZabT3gI5WU9cSmduABGAGrd8uIcmTZ5F2Ng9k6gFmRm7n8e8CULxDNu98ZpQfBjl7Bw== - dependencies: - "@jest/create-cache-key-function" "^29.6.3" - "@react-native-community/cli" "13.6.8" - "@react-native-community/cli-platform-android" "13.6.8" - "@react-native-community/cli-platform-ios" "13.6.8" - "@react-native/assets-registry" "0.74.84" - "@react-native/codegen" "0.74.84" - "@react-native/community-cli-plugin" "0.74.84" - "@react-native/gradle-plugin" "0.74.84" - "@react-native/js-polyfills" "0.74.84" - "@react-native/normalize-colors" "0.74.84" - "@react-native/virtualized-lists" "0.74.84" - abort-controller "^3.0.0" - anser "^1.4.9" - ansi-regex "^5.0.0" - base64-js "^1.5.1" - chalk "^4.0.0" - event-target-shim "^5.0.1" - flow-enums-runtime "^0.0.6" - invariant "^2.2.4" - jest-environment-node "^29.6.3" - jsc-android "^250231.0.0" - memoize-one "^5.0.0" - metro-runtime "^0.80.3" - metro-source-map "^0.80.3" - mkdirp "^0.5.1" - nullthrows "^1.1.1" - pretty-format "^26.5.2" - promise "^8.3.0" - react-devtools-core "^5.0.0" - react-refresh "^0.14.0" - react-shallow-renderer "^16.15.0" - regenerator-runtime "^0.13.2" - scheduler "0.24.0-canary-efb381bbf-20230505" - stacktrace-parser "^0.1.10" - whatwg-fetch "^3.0.0" - ws "^6.2.2" - yargs "^17.6.2" - react-oidc-context@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/react-oidc-context/-/react-oidc-context-2.3.1.tgz" integrity sha512-WdhmEU6odNzMk9pvOScxUkf6/1aduiI/nQryr7+iCl2VDnYLASDTIV/zy58KuK4VXG3fBaRKukc/mRpMjF9a3Q== -"react-redux@^7.2.1 || ^8.0.2", react-redux@^7.2.6: +react-redux@^6.0.1, react-redux@^7.2.6: version "7.2.9" resolved "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz" integrity sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ== @@ -12860,16 +11256,11 @@ react-oidc-context@^2.3.1: prop-types "^15.7.2" react-is "^17.0.2" -react-refresh@^0.11.0, "react-refresh@>=0.10.0 <1.0.0": +react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== -react-refresh@^0.14.0: - version "0.14.2" - resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz" - integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== - react-router-dom@6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.0.0.tgz" @@ -12939,14 +11330,6 @@ react-scripts@^5.0.1: optionalDependencies: fsevents "^2.3.2" -react-shallow-renderer@^16.15.0: - version "16.15.0" - resolved "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz" - integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== - dependencies: - object-assign "^4.1.1" - react-is "^16.12.0 || ^17.0.0 || ^18.0.0" - react-transition-group@^4.4.5: version "4.4.5" resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz" @@ -12957,7 +11340,7 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react@*, "react@^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0 || ^17.0 || ^18.0.0", "react@^15.0.0 || >=16.0.0", "react@^15.3.0 || 16 || 17 || 18", "react@^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.6.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.3 || ^17 || ^18", "react@^16.9.0 || ^17.0.0 || ^18", "react@^16.x || ^17.x || ^18.x", react@^17.0.0, "react@^17.0.0 || ^18.0.0", react@^17.0.2, "react@^17.0.2 || ^18.0.0", react@<18.0.0, "react@>= 16", "react@>= 16.8", react@>=0.13, react@>=0.13.x, react@>=0.14.x, react@>=15, react@>=16.3.0, react@>=16.6.0, react@>=16.8, react@>=16.8.0, "react@~0.13.x || ~0.14.x || ^15.0.0 || ^16.0.0 || ^17.0.0", react@17.0.2: +react@^17.0.2: version "17.0.2" resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz" integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== @@ -12965,13 +11348,6 @@ react@*, "react@^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0 || ^17.0 || ^18.0.0", loose-envify "^1.1.0" object-assign "^4.1.1" -react@18.2.0: - version "18.2.0" - resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" - integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== - dependencies: - loose-envify "^1.1.0" - read-cache@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" @@ -12992,7 +11368,7 @@ readable-stream@^2.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.6, readable-stream@^3.4.0: +readable-stream@^3.0.6: version "3.6.2" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -13001,19 +11377,6 @@ readable-stream@^3.0.6, readable-stream@^3.4.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@~2.3.6: - version "2.3.8" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" @@ -13021,21 +11384,6 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -readline@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz" - integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== - -recast@^0.21.0: - version "0.21.5" - resolved "https://registry.npmjs.org/recast/-/recast-0.21.5.tgz" - integrity sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg== - dependencies: - ast-types "0.15.2" - esprima "~4.0.0" - source-map "~0.6.1" - tslib "^2.0.1" - recursive-readdir@^2.2.2: version "2.2.3" resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz" @@ -13056,7 +11404,7 @@ redux-thunk@^2.3.0, redux-thunk@^2.4.2: resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz" integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q== -redux@^4, redux@^4.0.0, redux@^4.2.1: +redux@^4.0.0, redux@^4.2.1: version "4.2.1" resolved "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz" integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w== @@ -13088,11 +11436,6 @@ regenerate@^1.4.2: resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.2: - version "0.13.11" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== - regenerator-runtime@^0.13.9: version "0.13.11" resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" @@ -13175,11 +11518,6 @@ require-from-string@^2.0.2: resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - requires-port@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" @@ -13197,11 +11535,6 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz" - integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" @@ -13212,7 +11545,7 @@ resolve-from@^5.0.0: resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-url-loader@5.0.0: +resolve-url-loader@5.0.0, resolve-url-loader@^4.0.0: version "5.0.0" resolved "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz" integrity sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg== @@ -13251,14 +11584,6 @@ response-iterator@^0.2.6: resolved "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.6.tgz" integrity sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw== -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - retry@^0.13.1: version "0.13.1" resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" @@ -13281,13 +11606,6 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@~2.6.2: - version "2.6.3" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - rollup-plugin-terser@^7.0.0: version "7.0.2" resolved "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz" @@ -13298,7 +11616,7 @@ rollup-plugin-terser@^7.0.0: serialize-javascript "^4.0.0" terser "^5.0.0" -"rollup@^1.20.0 || ^2.0.0", rollup@^1.20.0||^2.0.0, rollup@^2.0.0, rollup@^2.43.1: +rollup@^2.43.1: version "2.79.1" resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz" integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== @@ -13322,21 +11640,16 @@ safe-array-concat@^1.1.2: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@^5.1.0, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.1.2: +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-regex-test@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz" @@ -13384,12 +11697,14 @@ scheduler@^0.20.2: loose-envify "^1.1.0" object-assign "^4.1.1" -scheduler@0.24.0-canary-efb381bbf-20230505: - version "0.24.0-canary-efb381bbf-20230505" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.24.0-canary-efb381bbf-20230505.tgz" - integrity sha512-ABvovCDe/k9IluqSh4/ISoq8tIJnW8euVAWYt5j/bg6dRnqwQwiGO1F/V4AyK96NGF/FB04FhOUDuWj8IKfABA== +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== dependencies: - loose-envify "^1.1.0" + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" schema-utils@^2.6.5: version "2.7.1" @@ -13400,25 +11715,7 @@ schema-utils@^2.6.5: ajv "^6.12.4" ajv-keywords "^3.5.2" -schema-utils@^3.0.0: - version "3.3.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" - integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== - dependencies: - "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - -schema-utils@^3.1.1: - version "3.3.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" - integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== - dependencies: - "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - -schema-utils@^3.2.0: +schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== @@ -13437,21 +11734,12 @@ schema-utils@^4.0.0, schema-utils@^4.2.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -schema-utils@2.7.0: - version "2.7.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" - integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== - dependencies: - "@types/json-schema" "^7.0.4" - ajv "^6.12.2" - ajv-keywords "^3.4.1" - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== -selfsigned@^2.1.1, selfsigned@^2.4.1: +selfsigned@^2.1.1: version "2.4.1" resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz" integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== @@ -13459,52 +11747,17 @@ selfsigned@^2.1.1, selfsigned@^2.4.1: "@types/node-forge" "^1.3.0" node-forge "^1" -semver@^5.5.0, semver@^5.6.0: +semver@^5.5.0: version "5.7.2" resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.0.0: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^6.3.0: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^6.3.1: +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2: - version "7.6.2" - resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== - -semver@^7.3.5: - version "7.6.2" - resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== - -semver@^7.3.7: - version "7.6.2" - resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== - -semver@^7.5.2: - version "7.6.2" - resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== - -semver@^7.5.3: - version "7.6.2" - resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== - -semver@^7.5.4: +semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4: version "7.6.2" resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== @@ -13528,11 +11781,6 @@ send@0.18.0: range-parser "~1.2.1" statuses "2.0.1" -serialize-error@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz" - integrity sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw== - serialize-javascript@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz" @@ -13560,7 +11808,7 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" -serve-static@^1.13.1, serve-static@1.15.0: +serve-static@1.15.0: version "1.15.0" resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== @@ -13570,11 +11818,6 @@ serve-static@^1.13.1, serve-static@1.15.0: parseurl "~1.3.3" send "0.18.0" -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== - set-function-length@^1.2.1: version "1.2.2" resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" @@ -13607,13 +11850,6 @@ setprototypeof@1.2.0: resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - shallowequal@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz" @@ -13643,7 +11879,7 @@ shebang-regex@^3.0.0: resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.6.1, shell-quote@^1.7.3, shell-quote@^1.8.1: +shell-quote@^1.7.3, shell-quote@^1.8.1: version "1.8.1" resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz" integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== @@ -13708,15 +11944,6 @@ slash@^4.0.0: resolved "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== -slice-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - sockjs@^0.3.24: version "0.3.24" resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz" @@ -13745,7 +11972,7 @@ source-map-loader@^3.0.0: iconv-lite "^0.6.3" source-map-js "^1.0.1" -source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@~0.5.20: +source-map-support@^0.5.6, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -13753,20 +11980,15 @@ source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@~0.5.2 buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.5.6, source-map@^0.5.7: - version "0.5.7" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" - integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== - -source-map@^0.6.0: +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== source-map@^0.7.3: version "0.7.4" @@ -13780,21 +12002,6 @@ source-map@^0.8.0-beta.0: dependencies: whatwg-url "^7.0.0" -source-map@~0.6.0: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - sourcemap-codec@^1.4.8: version "1.4.8" resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" @@ -13845,13 +12052,6 @@ stackframe@^1.3.4: resolved "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz" integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== -stacktrace-parser@^0.1.10: - version "0.1.10" - resolved "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz" - integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== - dependencies: - type-fest "^0.7.1" - state-local@^1.0.6: version "1.0.7" resolved "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz" @@ -13864,21 +12064,16 @@ static-eval@2.0.2: dependencies: escodegen "^1.8.1" -"statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - -statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - statuses@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + stop-iteration-iterator@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz" @@ -13886,20 +12081,6 @@ stop-iteration-iterator@^1.0.0: dependencies: internal-slot "^1.0.4" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - string-length@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" @@ -13921,16 +12102,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -13994,6 +12166,20 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz" @@ -14003,28 +12189,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^5.0.0: - version "5.2.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -14106,11 +12271,6 @@ sucrase@^3.32.0: pirates "^4.0.1" ts-interface-checker "^0.1.9" -sudo-prompt@^9.0.0: - version "9.2.1" - resolved "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz" - integrity sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw== - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" @@ -14118,14 +12278,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0: - version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -14178,7 +12331,7 @@ svg.filter.js@^2.0.2: dependencies: svg.js "^2.2.5" -svg.js@^2.0.1, svg.js@^2.2.5, svg.js@^2.4.0, svg.js@^2.6.5, svg.js@>=2.3.x: +svg.js@>=2.3.x, svg.js@^2.0.1, svg.js@^2.2.5, svg.js@^2.4.0, svg.js@^2.6.5: version "2.7.1" resolved "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz" integrity sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA== @@ -14302,13 +12455,6 @@ temp-dir@^2.0.0: resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz" integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== -temp@^0.8.4: - version "0.8.4" - resolved "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz" - integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== - dependencies: - rimraf "~2.6.2" - tempy@^0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz" @@ -14338,7 +12484,7 @@ terser-webpack-plugin@^5.2.5, terser-webpack-plugin@^5.3.10: serialize-javascript "^6.0.1" terser "^5.26.0" -terser@^5.0.0, terser@^5.10.0, terser@^5.15.0, terser@^5.26.0: +terser@^5.0.0, terser@^5.10.0, terser@^5.26.0: version "5.31.1" resolved "https://registry.npmjs.org/terser/-/terser-5.31.1.tgz" integrity sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg== @@ -14376,24 +12522,11 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" -throat@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz" - integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== - throat@^6.0.1: version "6.0.2" resolved "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz" integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== -through2@^2.0.1: - version "2.0.5" - resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - thunky@^1.0.2: version "1.1.0" resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" @@ -14499,31 +12632,21 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.10.0, tslib@^1.8.0, tslib@^1.9.3: - version "1.14.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^1.11.1, tslib@^1.8.0: - version "1.14.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== -tslib@^1.8.1: +tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.6.2: +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.6.2: version "2.6.3" resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== -tslib@2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz" - integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== - tsutils@^3.21.0: version "3.21.0" resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" @@ -14560,16 +12683,11 @@ type-fest@^0.20.2: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== -type-fest@^0.21.3, "type-fest@>=0.17.0 <5.0.0": +type-fest@^0.21.3: version "0.21.3" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@^0.7.1: - version "0.7.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz" - integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== - type-is@~1.6.18: version "1.6.18" resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" @@ -14629,11 +12747,6 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -"typescript@^3.2.1 || ^4", "typescript@>= 2.7", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta": - version "4.9.5" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== - ulid@2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz" @@ -14702,11 +12815,6 @@ universal-cookie@^4.0.4: "@types/cookie" "^0.3.3" cookie "^0.4.0" -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - universalify@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" @@ -14717,7 +12825,7 @@ universalify@^2.0.0: resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -unpipe@~1.0.0, unpipe@1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -14765,7 +12873,7 @@ url-search-params-polyfill@^7.0.0: resolved "https://registry.npmjs.org/url-search-params-polyfill/-/url-search-params-polyfill-7.0.1.tgz" integrity sha512-bAw7L2E+jn9XHG5P9zrPnHdO0yJub4U+yXJOdpcpkr7OBd9T8oll4lUos0iSGRcDvfZoLUKfx9a6aNmIhJ4+mQ== -url@^0.11.0, url@0.11.0: +url@0.11.0, url@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz" integrity sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ== @@ -14798,7 +12906,7 @@ utils-merge@1.0.1: resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@^3.0.0, uuid@^3.2.1, uuid@3.4.0: +uuid@3.4.0, uuid@^3.0.0, uuid@^3.2.1: version "3.4.0" resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== @@ -14822,11 +12930,6 @@ vary@~1.1.2: resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -vlq@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz" - integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== - w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" @@ -14871,13 +12974,6 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" - integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== - dependencies: - defaults "^1.0.3" - web-vitals@^2.1.4: version "2.1.4" resolved "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz" @@ -14903,7 +12999,7 @@ webidl-conversions@^6.1.0: resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== -webpack-dev-middleware@5.3.4: +webpack-dev-middleware@5.3.4, webpack-dev-middleware@^5.3.4: version "5.3.4" resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz" integrity sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q== @@ -14914,7 +13010,7 @@ webpack-dev-middleware@5.3.4: range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@^4.6.0, "webpack-dev-server@3.x || 4.x || 5.x": +webpack-dev-server@^4.6.0: version "4.15.2" resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz" integrity sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g== @@ -14979,7 +13075,7 @@ webpack-sources@^3.2.3: resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -"webpack@^4.0.0 || ^5.0.0", "webpack@^4.37.0 || ^5.0.0", "webpack@^4.4.0 || ^5.9.0", "webpack@^4.44.2 || ^5.47.0", webpack@^5.0.0, webpack@^5.1.0, webpack@^5.20.0, webpack@^5.64.4, "webpack@>= 4", webpack@>=2, "webpack@>=4.43.0 <6.0.0": +webpack@^5.64.4: version "5.91.0" resolved "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz" integrity sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw== @@ -15009,7 +13105,7 @@ webpack-sources@^3.2.3: watchpack "^2.4.1" webpack-sources "^3.2.3" -websocket-driver@^0.7.4, websocket-driver@>=0.5.1: +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== @@ -15030,7 +13126,7 @@ whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" -whatwg-fetch@^3.0.0, whatwg-fetch@^3.6.2: +whatwg-fetch@^3.6.2: version "3.6.20" resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz" integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== @@ -15066,16 +13162,7 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" -whatwg-url@^8.0.0: - version "8.7.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - -whatwg-url@^8.5.0: +whatwg-url@^8.0.0, whatwg-url@^8.5.0: version "8.7.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz" integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== @@ -15123,11 +13210,6 @@ which-collection@^1.0.1: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-module@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz" - integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== - which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: version "1.1.15" resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz" @@ -15327,25 +13409,7 @@ workbox-window@6.6.0: "@types/trusted-types" "^2.0.2" workbox-core "6.6.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -15368,15 +13432,6 @@ wrappy@1: resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write-file-atomic@^2.3.0: - version "2.4.3" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz" - integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - signal-exit "^3.0.2" - write-file-atomic@^3.0.0: version "3.0.3" resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" @@ -15387,24 +13442,10 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -ws@^6.2.2: - version "6.2.3" - resolved "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz" - integrity sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA== - dependencies: - async-limiter "~1.0.0" - -ws@^6.2.3: - version "6.2.3" - resolved "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz" - integrity sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA== - dependencies: - async-limiter "~1.0.0" - -ws@^7, ws@^7.4.6, ws@^7.5.1: - version "7.5.10" - resolved "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz" - integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== +ws@^7.4.6, ws@^8.13.0, ws@^8.17.1: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== xml-name-validator@^3.0.0: version "3.0.0" @@ -15424,16 +13465,6 @@ xss@^1.0.11: commander "^2.20.3" cssfilter "0.0.10" -xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== - y18n@^5.0.5: version "5.0.8" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" @@ -15449,51 +13480,16 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.2.1: - version "2.4.5" - resolved "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz" - integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== - yaml@^2.3.4: version "2.4.5" resolved "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz" integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs-parser@^20.2.2: version "20.2.9" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs@^15.1.0: - version "15.4.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - yargs@^16.2.0: version "16.2.0" resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" @@ -15507,19 +13503,6 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.6.2: - version "17.7.2" - resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" @@ -15538,6 +13521,14 @@ yup@^0.32.11: property-expr "^2.0.4" toposort "^2.0.2" +zen-observable-ts@0.8.19: + version "0.8.19" + resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.19.tgz" + integrity sha512-u1a2rpE13G+jSzrg3aiCqXU5tN2kw41b+cBZGmnc+30YimdkKiDj9bTowcB41eL77/17RF/h+393AuVgShyheQ== + dependencies: + tslib "^1.9.3" + zen-observable "^0.8.0" + zen-observable-ts@^0.8.21: version "0.8.21" resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz" @@ -15553,24 +13544,16 @@ zen-observable-ts@^1.2.5: dependencies: zen-observable "0.8.15" -zen-observable-ts@0.8.19: - version "0.8.19" - resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.19.tgz" - integrity sha512-u1a2rpE13G+jSzrg3aiCqXU5tN2kw41b+cBZGmnc+30YimdkKiDj9bTowcB41eL77/17RF/h+393AuVgShyheQ== - dependencies: - tslib "^1.9.3" - zen-observable "^0.8.0" +zen-observable@0.8.15, zen-observable@^0.8.0: + version "0.8.15" + resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz" + integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== zen-observable@^0.7.0: version "0.7.1" resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.7.1.tgz" integrity sha512-OI6VMSe0yeqaouIXtedC+F55Sr6r9ppS7+wTbSexkYdHbdt4ctTuPNXP/rwm7GTVI63YBc+EBT0b0tl7YnJLRg== -zen-observable@^0.8.0, zen-observable@0.8.15: - version "0.8.15" - resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz" - integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== - zen-push@0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/zen-push/-/zen-push-0.2.1.tgz" diff --git a/tests/modules/redshift_datasets/__init__.py b/tests/modules/redshift_datasets/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/modules/redshift_datasets/conftest.py b/tests/modules/redshift_datasets/conftest.py new file mode 100644 index 000000000..e6a5d76a2 --- /dev/null +++ b/tests/modules/redshift_datasets/conftest.py @@ -0,0 +1,239 @@ +import os +import pytest +import boto3 +from dataall.base.context import set_context, dispose_context, RequestContext + +from dataall.modules.redshift_datasets.services.redshift_connection_service import RedshiftConnectionService +from dataall.modules.redshift_datasets.services.redshift_dataset_service import RedshiftDatasetService +from dataall.modules.redshift_datasets.aws.redshift import RedshiftClient +from dataall.modules.redshift_datasets.aws.redshift_serverless import RedshiftServerlessClient +from dataall.modules.redshift_datasets.aws.redshift_data import RedshiftDataClient + +ENVNAME = os.environ.get('envname', 'pytest') + + +class MockRedshiftDataClient: + def get_redshift_connection_database(self, *args, **kwargs): + return True + + def list_redshift_schemas(self, *args, **kwargs): + return ['public', 'dev'] + + def list_redshift_tables(self, *args, **kwargs): + return [ + {'name': 'table1', 'type': 'TABLE'}, + {'name': 'table2', 'type': 'TABLE'}, + {'name': 'table3', 'type': 'TABLE'}, + {'name': 'table4', 'type': 'TABLE'}, + ] + + def list_redshift_table_columns(self, *args, **kwargs): + return [ + {'name': 'column1', 'type': 'VARCHAR', 'nullable': True}, + {'name': 'column2', 'type': 'INTEGER', 'nullable': False}, + {'name': 'column3', 'type': 'DOUBLE', 'nullable': True}, + {'name': 'column4', 'type': 'BOOLEAN', 'nullable': False}, + ] + + +class MockRedshiftClient: + def describe_cluster(self, *args, **kwargs): + return {'ClusterIdentifier': 'cluster_id_1', 'ClusterStatus': 'available'} + + +class MockRedshiftServerlessClient: + def get_namespace_by_id(self, *args, **kwargs): + return {'namespaceId': 'XXXXXXXXXXXXXX', 'namespaceName': 'namespace_name_1'} + + def list_workgroups_in_namespace(self, *args, **kwargs): + return [ + { + 'workgroupName': 'workgroup_name_1', + 'workgroupArn': 'arn:aws:redshift-serverless:eu-west-1:XXXXXXXXXXXXXX:workgroup/workgroup_name_1', + } + ] + + def get_workgroup_arn(self, *args, **kwargs): + return 'arn:aws:redshift-serverless:eu-west-1:XXXXXXXXXXXXXX:workgroup/workgroup_name_1' + + +@pytest.fixture(scope='module', autouse=True) +def patch_sts_remote_session(module_mocker): + module_mocker.patch( + 'dataall.base.aws.sts.SessionHelper.remote_session', + return_value=boto3.Session(), + ) + + +@pytest.fixture(scope='function') +def patch_redshift(mocker): + # autospec=True ensures methods called in the MockClient correspond to real client methods + mocker.patch.object( + RedshiftClient, + '__new__', + return_value=MockRedshiftClient(), + autospec=True, + ) + mocker.patch.object( + RedshiftDataClient, + '__new__', + return_value=MockRedshiftDataClient(), + autospec=True, + ) + mocker.patch.object( + RedshiftServerlessClient, + '__new__', + return_value=MockRedshiftServerlessClient(), + autospec=True, + ) + + +@pytest.fixture(scope='function') +def api_context_1(db, user, group): + yield set_context(RequestContext(db_engine=db, username=user.username, groups=[group.name], user_id=user.username)) + dispose_context() + + +@pytest.fixture(scope='function') +def api_context_2(db, user2, group2): + yield set_context( + RequestContext(db_engine=db, username=user2.username, groups=[group2.name], user_id=user2.username) + ) + dispose_context() + + +@pytest.fixture(scope='function') +def connection1_serverless(db, user, group, env_fixture, mocker): + # autospec=True ensures methods called in the MockClient correspond to real client methods + mocker.patch.object( + RedshiftClient, + '__new__', + return_value=MockRedshiftClient(), + autospec=True, + ) + mocker.patch.object( + RedshiftDataClient, + '__new__', + return_value=MockRedshiftDataClient(), + autospec=True, + ) + mocker.patch.object( + RedshiftServerlessClient, + '__new__', + return_value=MockRedshiftServerlessClient(), + autospec=True, + ) + set_context(RequestContext(db_engine=db, username=user.username, groups=[group.name], user_id=user.username)) + connection = RedshiftConnectionService.create_redshift_connection( + uri=env_fixture.environmentUri, + admin_group=group.name, + data={ + 'connectionName': 'connection1', + 'redshiftType': 'serverless', + 'clusterId': None, + 'nameSpaceId': 'XXXXXXXXXXXXXX', + 'workgroup': 'workgroup_name_1', + 'database': 'database_1', + 'redshiftUser': None, + 'secretArn': 'arn:aws:secretsmanager:*:111111111111:secret:secret-1', + }, + ) + dispose_context() + yield connection + set_context(RequestContext(db_engine=db, username=user.username, groups=[group.name], user_id=user.username)) + RedshiftConnectionService.delete_redshift_connection(uri=connection.connectionUri) + dispose_context() + + +@pytest.fixture(scope='function') +def connection2_cluster(db, user, group, env_fixture, mocker): + # autospec=True ensures methods called in the MockClient correspond to real client methods + mocker.patch.object( + RedshiftClient, + '__new__', + return_value=MockRedshiftClient(), + autospec=True, + ) + mocker.patch.object( + RedshiftDataClient, + '__new__', + return_value=MockRedshiftDataClient(), + autospec=True, + ) + mocker.patch.object( + RedshiftServerlessClient, + '__new__', + return_value=MockRedshiftServerlessClient(), + autospec=True, + ) + set_context(RequestContext(db_engine=db, username=user.username, groups=[group.name], user_id=user.username)) + connection = RedshiftConnectionService.create_redshift_connection( + uri=env_fixture.environmentUri, + admin_group=group.name, + data={ + 'connectionName': 'connection2', + 'redshiftType': 'cluster', + 'clusterId': 'cluster-id', + 'nameSpaceId': None, + 'workgroup': None, + 'database': 'database_1', + 'redshiftUser': None, + 'secretArn': 'arn:aws:secretsmanager:*:111111111111:secret:secret-2', + }, + ) + dispose_context() + yield connection + set_context(RequestContext(db_engine=db, username=user.username, groups=[group.name], user_id=user.username)) + RedshiftConnectionService.delete_redshift_connection(uri=connection.connectionUri) + dispose_context() + + +@pytest.fixture(scope='function') +def imported_redshift_dataset_1_no_tables(db, user, group, env_fixture, connection1_serverless): + set_context(RequestContext(db_engine=db, username=user.username, groups=[group.name], user_id=user.username)) + dataset = RedshiftDatasetService.import_redshift_dataset( + uri=env_fixture.environmentUri, + admin_group=group.name, + data={ + 'label': 'imported_redshift_dataset_1', + 'SamlAdminGroupName': group.name, + 'connectionUri': connection1_serverless.connectionUri, + 'schema': 'public', + }, + ) + dispose_context() + yield dataset + set_context(RequestContext(db_engine=db, username=user.username, groups=[group.name], user_id=user.username)) + RedshiftDatasetService.delete_redshift_dataset(uri=dataset.datasetUri) + dispose_context() + + +@pytest.fixture(scope='function') +def imported_redshift_dataset_2_with_tables(db, user, group, env_fixture, connection1_serverless): + set_context(RequestContext(db_engine=db, username=user.username, groups=[group.name], user_id=user.username)) + dataset = RedshiftDatasetService.import_redshift_dataset( + uri=env_fixture.environmentUri, + admin_group=group.name, + data={ + 'label': 'imported_redshift_dataset_2', + 'SamlAdminGroupName': group.name, + 'connectionUri': connection1_serverless.connectionUri, + 'schema': 'public', + 'tables': ['table1', 'table2'], + }, + ) + dispose_context() + yield dataset + set_context(RequestContext(db_engine=db, username=user.username, groups=[group.name], user_id=user.username)) + RedshiftDatasetService.delete_redshift_dataset(uri=dataset.datasetUri) + dispose_context() + + +@pytest.fixture(scope='function') +def imported_dataset_2_table_1(db, user, group, env_fixture, imported_redshift_dataset_2_with_tables): + set_context(RequestContext(db_engine=db, username=user.username, groups=[group.name], user_id=user.username)) + tables = RedshiftDatasetService.list_redshift_dataset_tables( + uri=imported_redshift_dataset_2_with_tables.datasetUri, filter={'term': 'table1'} + ) + dispose_context() + yield tables['nodes'][0] diff --git a/tests/modules/redshift_datasets/test_unit_redshift_connection_service.py b/tests/modules/redshift_datasets/test_unit_redshift_connection_service.py new file mode 100644 index 000000000..d71f3851c --- /dev/null +++ b/tests/modules/redshift_datasets/test_unit_redshift_connection_service.py @@ -0,0 +1,233 @@ +from assertpy import assert_that +from unittest.mock import MagicMock, patch +from .conftest import MockRedshiftClient, MockRedshiftDataClient, MockRedshiftServerlessClient +from dataall.modules.redshift_datasets.services.redshift_connection_service import RedshiftConnectionService + + +def test_create_redshift_connection_namespace_not_found(env_fixture, api_context_1, group, mocker): + # Given a namespace that does not exist + mocker.patch( + 'dataall.modules.redshift_datasets.aws.redshift_serverless.RedshiftServerlessClient.get_namespace_by_id', + return_value=None, + ) + # Then + assert_that(RedshiftConnectionService.create_redshift_connection).raises(Exception).when_called_with( + uri=env_fixture.environmentUri, + admin_group=group.name, + data={ + 'connectionName': 'connection3', + 'redshiftType': 'serverless', + 'clusterId': None, + 'nameSpaceId': 'not-existent-id', + 'workgroup': 'workgroup-id', + 'database': 'database_1', + 'redshiftUser': None, + 'secretArn': 'arn:aws:secretsmanager:*:111111111111:secret:secret-2', + }, + ).contains('Redshift namespaceId not-existent-id does not exist') + + +def test_create_redshift_connection_workgroup_not_in_namespace(env_fixture, api_context_1, group, mocker): + mocker.patch( + 'dataall.modules.redshift_datasets.aws.redshift_serverless.RedshiftServerlessClient.get_namespace_by_id', + return_value=MockRedshiftServerlessClient().get_namespace_by_id(), + ) + mocker.patch( + 'dataall.modules.redshift_datasets.aws.redshift_serverless.RedshiftServerlessClient.list_workgroups_in_namespace', + return_value=[], + ) + + # Then + assert_that(RedshiftConnectionService.create_redshift_connection).raises(Exception).when_called_with( + uri=env_fixture.environmentUri, + admin_group=group.name, + data={ + 'connectionName': 'connection3', + 'redshiftType': 'serverless', + 'clusterId': None, + 'nameSpaceId': 'not-existent-id', + 'workgroup': 'workgroup-id', + 'database': 'database_1', + 'redshiftUser': None, + 'secretArn': 'arn:aws:secretsmanager:*:111111111111:secret:secret-2', + }, + ).contains('Redshift workgroup workgroup-id does not exist or is not associated to namespace not-existent-id') + + +def test_create_redshift_connection_cluster_not_found(env_fixture, api_context_1, group, mocker): + # Given a redshift cluster id that does not exist + mocker.patch( + 'dataall.modules.redshift_datasets.aws.redshift.RedshiftClient.describe_cluster', + return_value=False, + ) + + # Then + assert_that(RedshiftConnectionService.create_redshift_connection).raises(Exception).when_called_with( + uri=env_fixture.environmentUri, + admin_group=group.name, + data={ + 'connectionName': 'connection3', + 'redshiftType': 'cluster', + 'clusterId': 'cluster-id', + 'nameSpaceId': None, + 'workgroup': None, + 'database': 'database_1', + 'redshiftUser': None, + 'secretArn': 'arn:aws:secretsmanager:*:111111111111:secret:secret-2', + }, + ).contains('Redshift cluster cluster-id does not exist or cannot be accessed with these parameters') + + +def test_create_redshift_connection_database_not_found(env_fixture, api_context_1, group, mocker): + # Given a redshift cluster id + mock_redshift = MagicMock() + mocker.patch( + 'dataall.modules.redshift_datasets.aws.redshift.RedshiftClient.describe_cluster', + return_value=mock_redshift, + autospec=True, + ) + mock_redshift.describe_cluster.return_value = MockRedshiftClient().describe_cluster() + mock_redshift_data = MagicMock() + mocker.patch( + 'dataall.modules.redshift_datasets.aws.redshift_data.RedshiftDataClient', + return_value=mock_redshift_data, + ) + mock_redshift_data.get_redshift_connection_database.side_effect = Exception + + # Then + assert_that(RedshiftConnectionService.create_redshift_connection).raises(Exception).when_called_with( + uri=env_fixture.environmentUri, + admin_group=group.name, + data={ + 'connectionName': 'connection3', + 'redshiftType': 'cluster', + 'clusterId': 'cluster-id', + 'nameSpaceId': None, + 'workgroup': None, + 'database': 'database_1', + 'redshiftUser': None, + 'secretArn': 'arn:aws:secretsmanager:*:111111111111:secret:secret-2', + }, + ).contains('Redshift database database_1 does not exist or cannot be accessed with these parameters') + + +def test_create_redshift_serverless_connection(connection1_serverless): + # When connection is created + # Then + assert_that(connection1_serverless).is_not_none() + assert_that(connection1_serverless.connectionUri).is_not_none() + assert_that(connection1_serverless.redshiftType).is_equal_to('serverless') + + +def test_create_redshift_cluster_connection(connection2_cluster): + # When connection is created + # Then + assert_that(connection2_cluster).is_not_none() + assert_that(connection2_cluster.connectionUri).is_not_none() + assert_that(connection2_cluster.redshiftType).is_equal_to('cluster') + + +def test_get_redshift_connection(connection1_serverless, api_context_1, patch_redshift): + # When + connection = RedshiftConnectionService.get_redshift_connection_by_uri(uri=connection1_serverless.connectionUri) + + # Then + assert_that(connection).is_not_none() + assert_that(connection.connectionUri).is_equal_to(connection1_serverless.connectionUri) + assert_that(connection.redshiftType).is_equal_to('serverless') + + +def test_get_redshift_connection_unauthorized(connection1_serverless, api_context_2, patch_redshift): + # When + assert_that(RedshiftConnectionService.get_redshift_connection_by_uri).raises(Exception).when_called_with( + uri=connection1_serverless.connectionUri + ).contains('UnauthorizedOperation', 'GET_REDSHIFT_CONNECTION', connection1_serverless.connectionUri) + + +def test_delete_redshift_connection(api_context_1, env_fixture, group, patch_redshift): + connection = RedshiftConnectionService.create_redshift_connection( + uri=env_fixture.environmentUri, + admin_group=group.name, + data={ + 'connectionName': 'connection-to-delete', + 'redshiftType': 'serverless', + 'clusterId': None, + 'nameSpaceId': 'XXXXXXXXXXXXXX', + 'workgroup': 'workgroup_name_1', + 'database': 'database_1', + 'redshiftUser': None, + 'secretArn': 'arn:aws:secretsmanager:*:111111111111:secret:secret-1', + }, + ) + # When + response = RedshiftConnectionService.delete_redshift_connection(uri=connection.connectionUri) + # Then + assert_that(response).is_true() + + +def test_delete_redshift_connection_unauthorized(connection1_serverless, api_context_2, patch_redshift): + # When + assert_that(RedshiftConnectionService.delete_redshift_connection).raises(Exception).when_called_with( + uri=connection1_serverless.connectionUri + ).contains('UnauthorizedOperation', 'DELETE_REDSHIFT_CONNECTION', connection1_serverless.connectionUri) + + +def test_list_environment_redshift_connections(connection1_serverless, connection2_cluster, api_context_1, env_fixture): + # When + response = RedshiftConnectionService.list_environment_redshift_connections( + uri=env_fixture.environmentUri, filter={} + ) + # Then + assert_that(response).contains_entry(count=2) + assert_that(response).contains_key('page', 'pages', 'pageSize', 'nodes', 'count') + connections = [conn.connectionUri for conn in response['nodes']] + assert_that(connections).is_equal_to([connection1_serverless.connectionUri, connection2_cluster.connectionUri]) + + +def test_list_environment_redshift_connections_with_filter( + connection1_serverless, connection2_cluster, api_context_1, env_fixture +): + # When + response = RedshiftConnectionService.list_environment_redshift_connections( + uri=env_fixture.environmentUri, filter={'term': connection1_serverless.name} + ) + # Then + assert_that(response).contains_entry(count=1) + assert_that(response).contains_key('page', 'pages', 'pageSize', 'nodes') + connections = [conn.connectionUri for conn in response['nodes']] + assert_that(connections).is_equal_to([connection1_serverless.connectionUri]) + + +def test_list_environment_redshift_connections_unauthorized( + connection1_serverless, connection2_cluster, api_context_2, env_fixture +): + # When + assert_that(RedshiftConnectionService.list_environment_redshift_connections).raises(Exception).when_called_with( + uri=env_fixture.environmentUri, filter={} + ).contains('UnauthorizedOperation', 'LIST_ENVIRONMENT_REDSHIFT_CONNECTIONS', env_fixture.environmentUri) + + +def test_list_connection_schemas(connection1_serverless, api_context_1, patch_redshift): + # When + response = RedshiftConnectionService.list_connection_schemas(uri=connection1_serverless.connectionUri) + assert_that(response).is_equal_to(MockRedshiftDataClient().list_redshift_schemas()) + + +def test_list_connection_schemas_unauthorized(connection1_serverless, api_context_2): + # When + assert_that(RedshiftConnectionService.list_connection_schemas).raises(Exception).when_called_with( + uri=connection1_serverless.connectionUri + ).contains('UnauthorizedOperation', 'GET_REDSHIFT_CONNECTION', connection1_serverless.connectionUri) + + +def test_list_schema_tables(connection1_serverless, api_context_1, patch_redshift): + # When + response = RedshiftConnectionService.list_schema_tables(uri=connection1_serverless.connectionUri, schema='schema1') + assert_that(response).is_equal_to(MockRedshiftDataClient().list_redshift_tables()) + + +def test_list_schema_tables_unauthorized(connection1_serverless, api_context_2): + # When + assert_that(RedshiftConnectionService.list_schema_tables).raises(Exception).when_called_with( + uri=connection1_serverless.connectionUri + ).contains('UnauthorizedOperation', 'GET_REDSHIFT_CONNECTION', connection1_serverless.connectionUri) diff --git a/tests/modules/redshift_datasets/test_unit_redshift_dataset_service.py b/tests/modules/redshift_datasets/test_unit_redshift_dataset_service.py new file mode 100644 index 000000000..2f9b58912 --- /dev/null +++ b/tests/modules/redshift_datasets/test_unit_redshift_dataset_service.py @@ -0,0 +1,230 @@ +from assertpy import assert_that +from dataall.modules.redshift_datasets.services.redshift_dataset_service import RedshiftDatasetService +from .conftest import MockRedshiftDataClient + + +def test_import_redshift_dataset_with_no_tables(imported_redshift_dataset_1_no_tables, api_context_1): + # When dataset is imported + # Then + assert_that(imported_redshift_dataset_1_no_tables).is_not_none() + assert_that(imported_redshift_dataset_1_no_tables.datasetUri).is_not_none() + assert_that(imported_redshift_dataset_1_no_tables.schema).is_equal_to('public') + # When we list the tables in this dataset + tables = RedshiftDatasetService.list_redshift_dataset_tables( + uri=imported_redshift_dataset_1_no_tables.datasetUri, filter={} + ) + # Then + assert_that(tables).contains_entry(count=0) + + +def test_import_redshift_dataset_with_tables(imported_redshift_dataset_2_with_tables, api_context_1): + # When dataset is imported + # Then + assert_that(imported_redshift_dataset_2_with_tables).is_not_none() + assert_that(imported_redshift_dataset_2_with_tables.datasetUri).is_not_none() + assert_that(imported_redshift_dataset_2_with_tables.schema).is_equal_to('public') + # When we list the tables in this dataset + tables = RedshiftDatasetService.list_redshift_dataset_tables( + uri=imported_redshift_dataset_2_with_tables.datasetUri, filter={} + ) + # Then + assert_that(tables).contains_entry(count=2) + + +def test_update_redshift_dataset_unauthorized(imported_redshift_dataset_1_no_tables, api_context_2): + # When + assert_that(RedshiftDatasetService.update_redshift_dataset).raises(Exception).when_called_with( + uri=imported_redshift_dataset_1_no_tables.datasetUri, data={'description': 'new description'} + ).contains('UnauthorizedOperation', 'UPDATE_REDSHIFT_DATASET', imported_redshift_dataset_1_no_tables.datasetUri) + + +def test_update_redshift_dataset(imported_redshift_dataset_1_no_tables, group3, group, api_context_1): + # When + dataset = RedshiftDatasetService.update_redshift_dataset( + uri=imported_redshift_dataset_1_no_tables.datasetUri, + data={'description': 'new description', 'stewards': group3.name}, + ) + # Then + assert_that(dataset.description).is_equal_to('new description') + assert_that(dataset.stewards).is_equal_to(group3.name) + # Revert stewards + dataset = RedshiftDatasetService.update_redshift_dataset( + uri=imported_redshift_dataset_1_no_tables.datasetUri, + data={'description': 'new description', 'stewards': group.name}, + ) + assert_that(dataset.stewards).is_equal_to(group.name) + + +def test_delete_redshift_dataset_unauthorized(imported_redshift_dataset_1_no_tables, api_context_2): + # When + assert_that(RedshiftDatasetService.delete_redshift_dataset).raises(Exception).when_called_with( + uri=imported_redshift_dataset_1_no_tables.datasetUri, + ).contains('UnauthorizedOperation', 'DELETE_REDSHIFT_DATASET', imported_redshift_dataset_1_no_tables.datasetUri) + + +def test_delete_redshift_dataset(env_fixture, group, connection1_serverless, api_context_1): + dataset = RedshiftDatasetService.import_redshift_dataset( + uri=env_fixture.environmentUri, + admin_group=group.name, + data={ + 'label': 'imported_redshift_to_delete', + 'SamlAdminGroupName': group.name, + 'connectionUri': connection1_serverless.connectionUri, + 'schema': 'public', + }, + ) + # When + response = RedshiftDatasetService.delete_redshift_dataset( + uri=dataset.datasetUri, + ) + # Then + assert_that(response).is_true() + + +def test_add_redshift_dataset_unauthorized(imported_redshift_dataset_1_no_tables, api_context_2): + # When + assert_that(RedshiftDatasetService.add_redshift_dataset_tables).raises(Exception).when_called_with( + uri=imported_redshift_dataset_1_no_tables.datasetUri, tables=['table3'] + ).contains('UnauthorizedOperation', 'ADD_TABLES_REDSHIFT_DATASET', imported_redshift_dataset_1_no_tables.datasetUri) + + +def test_add_redshift_dataset_tables(imported_redshift_dataset_1_no_tables, api_context_1): + # When + response = RedshiftDatasetService.add_redshift_dataset_tables( + uri=imported_redshift_dataset_1_no_tables.datasetUri, tables=['table3'] + ) + tables = RedshiftDatasetService.list_redshift_dataset_tables( + uri=imported_redshift_dataset_1_no_tables.datasetUri, filter={'term': 'table3'} + ) + # Then + assert_that(tables).contains_entry(count=1) + + +def test_delete_redshift_dataset_table_unauthorized(imported_dataset_2_table_1, api_context_2): + # When + assert_that(RedshiftDatasetService.delete_redshift_dataset_table).raises(Exception).when_called_with( + uri=imported_dataset_2_table_1.rsTableUri + ).contains('UnauthorizedOperation', 'DELETE_REDSHIFT_DATASET_TABLE', imported_dataset_2_table_1.rsTableUri) + + +def test_delete_redshift_dataset_table(imported_redshift_dataset_1_no_tables, api_context_1): + # Given` + response = RedshiftDatasetService.add_redshift_dataset_tables( + uri=imported_redshift_dataset_1_no_tables.datasetUri, tables=['table-to-delete'] + ) + tables = RedshiftDatasetService.list_redshift_dataset_tables( + uri=imported_redshift_dataset_1_no_tables.datasetUri, filter={'term': 'table-to-delete'} + ) + # When + response = RedshiftDatasetService.delete_redshift_dataset_table(uri=tables['nodes'][0].rsTableUri) + assert_that(response).is_true() + + +def test_update_redshift_dataset_table_unauthorized(imported_dataset_2_table_1, api_context_2): + # When + assert_that(RedshiftDatasetService.update_redshift_dataset_table).raises(Exception).when_called_with( + uri=imported_dataset_2_table_1.rsTableUri, data={'description': 'new description'} + ).contains('UnauthorizedOperation', 'UPDATE_REDSHIFT_DATASET_TABLE', imported_dataset_2_table_1.rsTableUri) + + +def test_update_redshift_dataset_table(imported_dataset_2_table_1, api_context_1): + # When + table = RedshiftDatasetService.update_redshift_dataset_table( + uri=imported_dataset_2_table_1.rsTableUri, data={'description': 'new description'} + ) + # Then + assert_that(table.description).is_equal_to('new description') + + +def test_get_redshift_dataset_unauthorized(imported_redshift_dataset_1_no_tables, api_context_2): + # When + assert_that(RedshiftDatasetService.get_redshift_dataset).raises(Exception).when_called_with( + uri=imported_redshift_dataset_1_no_tables.datasetUri + ).contains('UnauthorizedOperation', 'GET_REDSHIFT_DATASET', imported_redshift_dataset_1_no_tables.datasetUri) + + +def test_get_redshift_dataset(imported_redshift_dataset_1_no_tables, api_context_1): + # When + dataset = RedshiftDatasetService.get_redshift_dataset(uri=imported_redshift_dataset_1_no_tables.datasetUri) + # Then + assert_that(dataset.datasetUri).is_equal_to(imported_redshift_dataset_1_no_tables.datasetUri) + assert_that(dataset.schema).is_equal_to('public') + + +def test_list_redshift_dataset_tables_unauthorized(imported_redshift_dataset_1_no_tables, api_context_2): + # When + assert_that(RedshiftDatasetService.list_redshift_dataset_tables).raises(Exception).when_called_with( + uri=imported_redshift_dataset_1_no_tables.datasetUri, filter={} + ).contains('UnauthorizedOperation', 'GET_REDSHIFT_DATASET', imported_redshift_dataset_1_no_tables.datasetUri) + + +def test_list_redshift_dataset_tables(imported_redshift_dataset_2_with_tables, api_context_1): + # When + response = RedshiftDatasetService.list_redshift_dataset_tables( + uri=imported_redshift_dataset_2_with_tables.datasetUri, filter={} + ) + # Then + assert_that(response).contains_key('count', 'page', 'pages', 'nodes') + + +def test_list_redshift_schema_dataset_tables_unauthorized(imported_redshift_dataset_1_no_tables, api_context_2): + # When + assert_that(RedshiftDatasetService.list_redshift_schema_dataset_tables).raises(Exception).when_called_with( + uri=imported_redshift_dataset_1_no_tables.datasetUri + ).contains('UnauthorizedOperation', 'GET_REDSHIFT_DATASET', imported_redshift_dataset_1_no_tables.datasetUri) + + +def test_list_redshift_schema_dataset_tables(imported_redshift_dataset_1_no_tables, patch_redshift, api_context_1): + # When + tables = RedshiftDatasetService.list_redshift_schema_dataset_tables( + uri=imported_redshift_dataset_1_no_tables.datasetUri + ) + # Then + assert_that(tables).is_not_none() + assert_that(tables[0]).contains_key('alreadyAdded', 'type', 'name') + + +def test_get_dataset_upvotes_unauthorized(imported_redshift_dataset_1_no_tables, api_context_2): + # When + assert_that(RedshiftDatasetService.get_dataset_upvotes).raises(Exception).when_called_with( + uri=imported_redshift_dataset_1_no_tables.datasetUri + ).contains('UnauthorizedOperation', 'GET_REDSHIFT_DATASET', imported_redshift_dataset_1_no_tables.datasetUri) + + +def test_get_dataset_upvotes(imported_redshift_dataset_1_no_tables, api_context_1): + # When + response = RedshiftDatasetService.get_dataset_upvotes(uri=imported_redshift_dataset_1_no_tables.datasetUri) + # Then + assert_that(response).is_equal_to(0) + + +def test_get_redshift_dataset_table_unauthorized(imported_dataset_2_table_1, api_context_2): + # When + assert_that(RedshiftDatasetService.get_redshift_dataset_table).raises(Exception).when_called_with( + uri=imported_dataset_2_table_1.rsTableUri + ).contains('UnauthorizedOperation', 'GET_REDSHIFT_DATASET_TABLE', imported_dataset_2_table_1.rsTableUri) + + +def test_get_redshift_dataset_table(imported_dataset_2_table_1, api_context_1): + # When + table = RedshiftDatasetService.get_redshift_dataset_table(uri=imported_dataset_2_table_1.rsTableUri) + # Then + assert_that(table.rsTableUri).is_equal_to(imported_dataset_2_table_1.rsTableUri) + assert_that(table.name).is_equal_to('table1') + + +def test_list_redshift_dataset_table_columns_unauthorized(imported_dataset_2_table_1, api_context_2): + # When + assert_that(RedshiftDatasetService.list_redshift_dataset_table_columns).raises(Exception).when_called_with( + uri=imported_dataset_2_table_1.rsTableUri, filter={} + ).contains('UnauthorizedOperation', 'GET_REDSHIFT_DATASET_TABLE', imported_dataset_2_table_1.rsTableUri) + + +def test_list_redshift_dataset_table_columns(imported_dataset_2_table_1, api_context_1): + # When + response = RedshiftDatasetService.list_redshift_dataset_table_columns( + uri=imported_dataset_2_table_1.rsTableUri, filter={} + ) + # Then + assert_that(response).contains_key('count', 'page', 'pages', 'nodes') + assert_that(response['nodes']).is_equal_to(MockRedshiftDataClient().list_redshift_table_columns()) diff --git a/tests/requirements.txt b/tests/requirements.txt index c19b64d12..87c1432ef 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -3,4 +3,5 @@ pytest==7.3.1 pytest-cov==3.0.0 pytest-mock==3.6.1 pytest-dependency==0.5.1 -werkzeug==3.0.3 \ No newline at end of file +werkzeug==3.0.3 +assertpy==1.1.0 \ No newline at end of file