diff --git a/.github/workflows/operator_pr.yml b/.github/workflows/operator_pr.yml new file mode 100644 index 00000000000..e4d371b9454 --- /dev/null +++ b/.github/workflows/operator_pr.yml @@ -0,0 +1,18 @@ +name: operator-pr + +on: [pull_request] +jobs: + operator-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Go + uses: actions/setup-go@v2 + with: + go-version: 1.21.x + - name: Operator tests + run: | + cd infra/feast-operator/ + make test + - name: After code formatting, check for uncommitted differences + run: git diff --exit-code infra/feast-operator diff --git a/community/ADOPTERS.md b/community/ADOPTERS.md index a16fbef3797..5ef285b41b8 100644 --- a/community/ADOPTERS.md +++ b/community/ADOPTERS.md @@ -11,5 +11,6 @@ alphabetical order. | Get Ground | Zhiling Chen | zhilingc | | Gojek | Pradithya Aria Pura | pradithya | | Twitter | David Liu | mavysavydav| +| SeatGeek | Rob Howley | robhowley | | Shopify | Matt Delacour | MattDelac | | Snowflake | Miles Adkins | sfc-gh-madkins | diff --git a/docs/getting-started/concepts/permission.md b/docs/getting-started/concepts/permission.md index 5bca1bd5685..a6353579687 100644 --- a/docs/getting-started/concepts/permission.md +++ b/docs/getting-started/concepts/permission.md @@ -36,7 +36,7 @@ The permission model is based on the following components: The `Permission` class identifies a single permission configured on the feature store and is identified by these attributes: - `name`: The permission name. - `types`: The list of protected resource types. Defaults to all managed types, e.g. the `ALL_RESOURCE_TYPES` alias. All sub-classes are included in the resource match. -- `name_pattern`: A regex to match the resource name. Defaults to `None`, meaning that no name filtering is applied +- `name_patterns`: A list of regex patterns to match resource names. If any regex matches, the `Permission` policy is applied. Defaults to `[]`, meaning no name filtering is applied. - `required_tags`: Dictionary of key-value pairs that must match the resource tags. Defaults to `None`, meaning that no tags filtering is applied. - `actions`: The actions authorized by this permission. Defaults to `ALL_VALUES`, an alias defined in the `action` module. - `policy`: The policy to be applied to validate a client request. @@ -95,7 +95,7 @@ The following permission grants authorization to read the offline store of all t Permission( name="reader", types=[FeatureView], - name_pattern=".*risky.*", + name_patterns=".*risky.*", # Accepts both `str` or `list[str]` types policy=RoleBasedPolicy(roles=["trusted"]), actions=[AuthzedAction.READ_OFFLINE], ) diff --git a/docs/reference/feast-cli-commands.md b/docs/reference/feast-cli-commands.md index b32db3215ae..8f1a7c302e6 100644 --- a/docs/reference/feast-cli-commands.md +++ b/docs/reference/feast-cli-commands.md @@ -172,9 +172,10 @@ Options: ```text +-----------------------+-------------+-----------------------+-----------+----------------+-------------------------+ -| NAME | TYPES | NAME_PATTERN | ACTIONS | ROLES | REQUIRED_TAGS | +| NAME | TYPES | NAME_PATTERNS | ACTIONS | ROLES | REQUIRED_TAGS | +=======================+=============+=======================+===========+================+================+========+ | reader_permission1234 | FeatureView | transformed_conv_rate | DESCRIBE | reader | - | +| | | driver_hourly_stats | DESCRIBE | reader | - | +-----------------------+-------------+-----------------------+-----------+----------------+-------------------------+ | writer_permission1234 | FeatureView | transformed_conv_rate | CREATE | writer | - | +-----------------------+-------------+-----------------------+-----------+----------------+-------------------------+ diff --git a/protos/feast/core/Permission.proto b/protos/feast/core/Permission.proto index 400f70a11bc..8a876a0dc78 100644 --- a/protos/feast/core/Permission.proto +++ b/protos/feast/core/Permission.proto @@ -50,7 +50,7 @@ message PermissionSpec { repeated Type types = 3; - string name_pattern = 4; + repeated string name_patterns = 4; map required_tags = 5; diff --git a/sdk/python/feast/cli.py b/sdk/python/feast/cli.py index 499788101e1..010493f01cb 100644 --- a/sdk/python/feast/cli.py +++ b/sdk/python/feast/cli.py @@ -1211,7 +1211,7 @@ def feast_permissions_list_command(ctx: click.Context, verbose: bool, tags: list headers=[ "NAME", "TYPES", - "NAME_PATTERN", + "NAME_PATTERNS", "ACTIONS", "ROLES", "REQUIRED_TAGS", diff --git a/sdk/python/feast/cli_utils.py b/sdk/python/feast/cli_utils.py index 4152eb219b2..38ebf915703 100644 --- a/sdk/python/feast/cli_utils.py +++ b/sdk/python/feast/cli_utils.py @@ -196,7 +196,7 @@ def handle_not_verbose_permissions_command( [ p.name, _to_multi_line([t.__name__ for t in p.types]), # type: ignore[union-attr, attr-defined] - p.name_pattern, + _to_multi_line(p.name_patterns), _to_multi_line([a.value.upper() for a in p.actions]), _to_multi_line(sorted(roles)), _dict_to_multi_line(p.required_tags), diff --git a/sdk/python/feast/feature_server.py b/sdk/python/feast/feature_server.py index c188bd0d7b4..ab88eca89fc 100644 --- a/sdk/python/feast/feature_server.py +++ b/sdk/python/feast/feature_server.py @@ -10,6 +10,7 @@ import psutil from dateutil import parser from fastapi import Depends, FastAPI, Request, Response, status +from fastapi.concurrency import run_in_threadpool from fastapi.logger import logger from fastapi.responses import JSONResponse from google.protobuf.json_format import MessageToDict @@ -112,7 +113,7 @@ async def get_body(request: Request): "/get-online-features", dependencies=[Depends(inject_user_details)], ) - def get_online_features(body=Depends(get_body)): + async def get_online_features(body=Depends(get_body)): body = json.loads(body) full_feature_names = body.get("full_feature_names", False) entity_rows = body["entities"] @@ -145,15 +146,22 @@ def get_online_features(body=Depends(get_body)): resource=od_feature_view, actions=[AuthzedAction.READ_ONLINE] ) - response_proto = store.get_online_features( + read_params = dict( features=features, entity_rows=entity_rows, full_feature_names=full_feature_names, - ).proto + ) + + if store._get_provider().async_supported.online.read: + response = await store.get_online_features_async(**read_params) + else: + response = await run_in_threadpool( + lambda: store.get_online_features(**read_params) + ) # Convert the Protobuf object to JSON and return it return MessageToDict( - response_proto, preserving_proto_field_name=True, float_precision=18 + response.proto, preserving_proto_field_name=True, float_precision=18 ) @app.post("/push", dependencies=[Depends(inject_user_details)]) diff --git a/sdk/python/feast/feature_store.py b/sdk/python/feast/feature_store.py index 9a652fd0608..9b1a35303ea 100644 --- a/sdk/python/feast/feature_store.py +++ b/sdk/python/feast/feature_store.py @@ -713,7 +713,7 @@ def plan( >>> fs = FeatureStore(repo_path="project/feature_repo") >>> driver = Entity(name="driver_id", description="driver id") >>> driver_hourly_stats = FileSource( - ... path="project/feature_repo/data/driver_stats.parquet", + ... path="data/driver_stats.parquet", ... timestamp_field="event_timestamp", ... created_timestamp_column="created", ... ) @@ -827,7 +827,7 @@ def apply( >>> fs = FeatureStore(repo_path="project/feature_repo") >>> driver = Entity(name="driver_id", description="driver id") >>> driver_hourly_stats = FileSource( - ... path="project/feature_repo/data/driver_stats.parquet", + ... path="data/driver_stats.parquet", ... timestamp_field="event_timestamp", ... created_timestamp_column="created", ... ) diff --git a/sdk/python/feast/infra/offline_stores/dask.py b/sdk/python/feast/infra/offline_stores/dask.py index 52ad88d2997..d26e8609bae 100644 --- a/sdk/python/feast/infra/offline_stores/dask.py +++ b/sdk/python/feast/infra/offline_stores/dask.py @@ -57,6 +57,7 @@ def __init__( self, evaluation_function: Callable, full_feature_names: bool, + repo_path: str, on_demand_feature_views: Optional[List[OnDemandFeatureView]] = None, metadata: Optional[RetrievalMetadata] = None, ): @@ -67,6 +68,7 @@ def __init__( self._full_feature_names = full_feature_names self._on_demand_feature_views = on_demand_feature_views or [] self._metadata = metadata + self.repo_path = repo_path @property def full_feature_names(self) -> bool: @@ -99,8 +101,13 @@ def persist( if not allow_overwrite and os.path.exists(storage.file_options.uri): raise SavedDatasetLocationAlreadyExists(location=storage.file_options.uri) + if not Path(storage.file_options.uri).is_absolute(): + absolute_path = Path(self.repo_path) / storage.file_options.uri + else: + absolute_path = Path(storage.file_options.uri) + filesystem, path = FileSource.create_filesystem_and_path( - storage.file_options.uri, + str(absolute_path), storage.file_options.s3_endpoint_override, ) @@ -243,7 +250,9 @@ def evaluate_historical_retrieval(): all_join_keys = list(set(all_join_keys + join_keys)) - df_to_join = _read_datasource(feature_view.batch_source) + df_to_join = _read_datasource( + feature_view.batch_source, config.repo_path + ) df_to_join, timestamp_field = _field_mapping( df_to_join, @@ -297,6 +306,7 @@ def evaluate_historical_retrieval(): min_event_timestamp=entity_df_event_timestamp_range[0], max_event_timestamp=entity_df_event_timestamp_range[1], ), + repo_path=str(config.repo_path), ) return job @@ -316,7 +326,7 @@ def pull_latest_from_table_or_query( # Create lazy function that is only called from the RetrievalJob object def evaluate_offline_job(): - source_df = _read_datasource(data_source) + source_df = _read_datasource(data_source, config.repo_path) source_df = _normalize_timestamp( source_df, timestamp_field, created_timestamp_column @@ -377,6 +387,7 @@ def evaluate_offline_job(): return DaskRetrievalJob( evaluation_function=evaluate_offline_job, full_feature_names=False, + repo_path=str(config.repo_path), ) @staticmethod @@ -420,8 +431,13 @@ def write_logged_features( # Since this code will be mostly used from Go-created thread, it's better to avoid producing new threads data = pyarrow.parquet.read_table(data, use_threads=False, pre_buffer=False) + if config.repo_path is not None and not Path(destination.path).is_absolute(): + absolute_path = config.repo_path / destination.path + else: + absolute_path = Path(destination.path) + filesystem, path = FileSource.create_filesystem_and_path( - destination.path, + str(absolute_path), destination.s3_endpoint_override, ) @@ -456,8 +472,14 @@ def offline_write_batch( ) file_options = feature_view.batch_source.file_options + + if config.repo_path is not None and not Path(file_options.uri).is_absolute(): + absolute_path = config.repo_path / file_options.uri + else: + absolute_path = Path(file_options.uri) + filesystem, path = FileSource.create_filesystem_and_path( - file_options.uri, file_options.s3_endpoint_override + str(absolute_path), file_options.s3_endpoint_override ) prev_table = pyarrow.parquet.read_table( path, filesystem=filesystem, memory_map=True @@ -493,7 +515,7 @@ def _get_entity_df_event_timestamp_range( ) -def _read_datasource(data_source) -> dd.DataFrame: +def _read_datasource(data_source, repo_path) -> dd.DataFrame: storage_options = ( { "client_kwargs": { @@ -504,8 +526,12 @@ def _read_datasource(data_source) -> dd.DataFrame: else None ) + if not Path(data_source.path).is_absolute(): + path = repo_path / data_source.path + else: + path = data_source.path return dd.read_parquet( - data_source.path, + path, storage_options=storage_options, ) diff --git a/sdk/python/feast/infra/offline_stores/duckdb.py b/sdk/python/feast/infra/offline_stores/duckdb.py index a639d54add5..e64da029a6a 100644 --- a/sdk/python/feast/infra/offline_stores/duckdb.py +++ b/sdk/python/feast/infra/offline_stores/duckdb.py @@ -27,7 +27,7 @@ from feast.repo_config import FeastConfigBaseModel, RepoConfig -def _read_data_source(data_source: DataSource) -> Table: +def _read_data_source(data_source: DataSource, repo_path: str) -> Table: assert isinstance(data_source, FileSource) if isinstance(data_source.file_format, ParquetFormat): @@ -43,6 +43,7 @@ def _read_data_source(data_source: DataSource) -> Table: def _write_data_source( table: Table, data_source: DataSource, + repo_path: str, mode: str = "append", allow_overwrite: bool = False, ): @@ -50,14 +51,24 @@ def _write_data_source( file_options = data_source.file_options - if mode == "overwrite" and not allow_overwrite and os.path.exists(file_options.uri): + if not Path(file_options.uri).is_absolute(): + absolute_path = Path(repo_path) / file_options.uri + else: + absolute_path = Path(file_options.uri) + + if ( + mode == "overwrite" + and not allow_overwrite + and os.path.exists(str(absolute_path)) + ): raise SavedDatasetLocationAlreadyExists(location=file_options.uri) if isinstance(data_source.file_format, ParquetFormat): if mode == "overwrite": table = table.to_pyarrow() + filesystem, path = FileSource.create_filesystem_and_path( - file_options.uri, + str(absolute_path), file_options.s3_endpoint_override, ) diff --git a/sdk/python/feast/infra/offline_stores/file_source.py b/sdk/python/feast/infra/offline_stores/file_source.py index 3fdc6cba31a..9557b8077d0 100644 --- a/sdk/python/feast/infra/offline_stores/file_source.py +++ b/sdk/python/feast/infra/offline_stores/file_source.py @@ -1,3 +1,4 @@ +from pathlib import Path from typing import Callable, Dict, Iterable, List, Optional, Tuple import pyarrow @@ -154,8 +155,16 @@ def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: def get_table_column_names_and_types( self, config: RepoConfig ) -> Iterable[Tuple[str, str]]: + if ( + config.repo_path is not None + and not Path(self.file_options.uri).is_absolute() + ): + absolute_path = config.repo_path / self.file_options.uri + else: + absolute_path = Path(self.file_options.uri) + filesystem, path = FileSource.create_filesystem_and_path( - self.path, self.file_options.s3_endpoint_override + str(absolute_path), self.file_options.s3_endpoint_override ) # TODO why None check necessary diff --git a/sdk/python/feast/infra/offline_stores/ibis.py b/sdk/python/feast/infra/offline_stores/ibis.py index 61c477baec6..66d00ca6292 100644 --- a/sdk/python/feast/infra/offline_stores/ibis.py +++ b/sdk/python/feast/infra/offline_stores/ibis.py @@ -46,8 +46,8 @@ def pull_latest_from_table_or_query_ibis( created_timestamp_column: Optional[str], start_date: datetime, end_date: datetime, - data_source_reader: Callable[[DataSource], Table], - data_source_writer: Callable[[pyarrow.Table, DataSource], None], + data_source_reader: Callable[[DataSource, str], Table], + data_source_writer: Callable[[pyarrow.Table, DataSource, str], None], staging_location: Optional[str] = None, staging_location_endpoint_override: Optional[str] = None, ) -> RetrievalJob: @@ -57,7 +57,7 @@ def pull_latest_from_table_or_query_ibis( start_date = start_date.astimezone(tz=timezone.utc) end_date = end_date.astimezone(tz=timezone.utc) - table = data_source_reader(data_source) + table = data_source_reader(data_source, str(config.repo_path)) table = table.select(*fields) @@ -87,6 +87,7 @@ def pull_latest_from_table_or_query_ibis( data_source_writer=data_source_writer, staging_location=staging_location, staging_location_endpoint_override=staging_location_endpoint_override, + repo_path=str(config.repo_path), ) @@ -147,8 +148,8 @@ def get_historical_features_ibis( entity_df: Union[pd.DataFrame, str], registry: BaseRegistry, project: str, - data_source_reader: Callable[[DataSource], Table], - data_source_writer: Callable[[pyarrow.Table, DataSource], None], + data_source_reader: Callable[[DataSource, str], Table], + data_source_writer: Callable[[pyarrow.Table, DataSource, str], None], full_feature_names: bool = False, staging_location: Optional[str] = None, staging_location_endpoint_override: Optional[str] = None, @@ -174,7 +175,9 @@ def get_historical_features_ibis( def read_fv( feature_view: FeatureView, feature_refs: List[str], full_feature_names: bool ) -> Tuple: - fv_table: Table = data_source_reader(feature_view.batch_source) + fv_table: Table = data_source_reader( + feature_view.batch_source, str(config.repo_path) + ) for old_name, new_name in feature_view.batch_source.field_mapping.items(): if old_name in fv_table.columns: @@ -247,6 +250,7 @@ def read_fv( data_source_writer=data_source_writer, staging_location=staging_location, staging_location_endpoint_override=staging_location_endpoint_override, + repo_path=str(config.repo_path), ) @@ -258,8 +262,8 @@ def pull_all_from_table_or_query_ibis( timestamp_field: str, start_date: datetime, end_date: datetime, - data_source_reader: Callable[[DataSource], Table], - data_source_writer: Callable[[pyarrow.Table, DataSource], None], + data_source_reader: Callable[[DataSource, str], Table], + data_source_writer: Callable[[pyarrow.Table, DataSource, str], None], staging_location: Optional[str] = None, staging_location_endpoint_override: Optional[str] = None, ) -> RetrievalJob: @@ -267,7 +271,7 @@ def pull_all_from_table_or_query_ibis( start_date = start_date.astimezone(tz=timezone.utc) end_date = end_date.astimezone(tz=timezone.utc) - table = data_source_reader(data_source) + table = data_source_reader(data_source, str(config.repo_path)) table = table.select(*fields) @@ -290,6 +294,7 @@ def pull_all_from_table_or_query_ibis( data_source_writer=data_source_writer, staging_location=staging_location, staging_location_endpoint_override=staging_location_endpoint_override, + repo_path=str(config.repo_path), ) @@ -319,7 +324,7 @@ def offline_write_batch_ibis( feature_view: FeatureView, table: pyarrow.Table, progress: Optional[Callable[[int], Any]], - data_source_writer: Callable[[pyarrow.Table, DataSource], None], + data_source_writer: Callable[[pyarrow.Table, DataSource, str], None], ): pa_schema, column_names = get_pyarrow_schema_from_batch_source( config, feature_view.batch_source @@ -330,7 +335,9 @@ def offline_write_batch_ibis( f"The schema is expected to be {pa_schema} with the columns (in this exact order) to be {column_names}." ) - data_source_writer(ibis.memtable(table), feature_view.batch_source) + data_source_writer( + ibis.memtable(table), feature_view.batch_source, str(config.repo_path) + ) def deduplicate( @@ -469,6 +476,7 @@ def __init__( data_source_writer, staging_location, staging_location_endpoint_override, + repo_path, ) -> None: super().__init__() self.table = table @@ -480,6 +488,7 @@ def __init__( self.data_source_writer = data_source_writer self.staging_location = staging_location self.staging_location_endpoint_override = staging_location_endpoint_override + self.repo_path = repo_path def _to_df_internal(self, timeout: Optional[int] = None) -> pd.DataFrame: return self.table.execute() @@ -502,7 +511,11 @@ def persist( timeout: Optional[int] = None, ): self.data_source_writer( - self.table, storage.to_data_source(), "overwrite", allow_overwrite + self.table, + storage.to_data_source(), + self.repo_path, + "overwrite", + allow_overwrite, ) @property diff --git a/sdk/python/feast/infra/online_stores/dynamodb.py b/sdk/python/feast/infra/online_stores/dynamodb.py index a6cbfb41d2b..a915d2ee34b 100644 --- a/sdk/python/feast/infra/online_stores/dynamodb.py +++ b/sdk/python/feast/infra/online_stores/dynamodb.py @@ -23,6 +23,7 @@ from feast.infra.infra_object import DYNAMODB_INFRA_OBJECT_CLASS_TYPE, InfraObject from feast.infra.online_stores.helpers import compute_entity_id from feast.infra.online_stores.online_store import OnlineStore +from feast.infra.supported_async_methods import SupportedAsyncMethods from feast.protos.feast.core.DynamoDBTable_pb2 import ( DynamoDBTable as DynamoDBTableProto, ) @@ -88,6 +89,10 @@ class DynamoDBOnlineStore(OnlineStore): _dynamodb_resource = None _aioboto_session = None + @property + def async_supported(self) -> SupportedAsyncMethods: + return SupportedAsyncMethods(read=True) + def update( self, config: RepoConfig, diff --git a/sdk/python/feast/infra/online_stores/online_store.py b/sdk/python/feast/infra/online_stores/online_store.py index cf2d68eb746..be2065040b6 100644 --- a/sdk/python/feast/infra/online_stores/online_store.py +++ b/sdk/python/feast/infra/online_stores/online_store.py @@ -22,6 +22,7 @@ from feast.feature_view import FeatureView from feast.infra.infra_object import InfraObject from feast.infra.registry.base_registry import BaseRegistry +from feast.infra.supported_async_methods import SupportedAsyncMethods from feast.online_response import OnlineResponse from feast.protos.feast.core.Registry_pb2 import Registry as RegistryProto from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto @@ -36,6 +37,10 @@ class OnlineStore(ABC): The interface that Feast uses to interact with the storage system that handles online features. """ + @property + def async_supported(self) -> SupportedAsyncMethods: + return SupportedAsyncMethods() + @abstractmethod def online_write_batch( self, diff --git a/sdk/python/feast/infra/passthrough_provider.py b/sdk/python/feast/infra/passthrough_provider.py index 5acfc0d6f34..ea75cf5ff20 100644 --- a/sdk/python/feast/infra/passthrough_provider.py +++ b/sdk/python/feast/infra/passthrough_provider.py @@ -35,6 +35,7 @@ from feast.infra.online_stores.helpers import get_online_store_from_config from feast.infra.provider import Provider from feast.infra.registry.base_registry import BaseRegistry +from feast.infra.supported_async_methods import ProviderAsyncMethods from feast.online_response import OnlineResponse from feast.protos.feast.core.Registry_pb2 import Registry as RegistryProto from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto @@ -79,6 +80,12 @@ def offline_store(self): ) return self._offline_store + @property + def async_supported(self) -> ProviderAsyncMethods: + return ProviderAsyncMethods( + online=self.online_store.async_supported, + ) + @property def batch_engine(self) -> BatchMaterializationEngine: if self._batch_engine: diff --git a/sdk/python/feast/infra/provider.py b/sdk/python/feast/infra/provider.py index 0723e0513fd..fb483d194ed 100644 --- a/sdk/python/feast/infra/provider.py +++ b/sdk/python/feast/infra/provider.py @@ -27,6 +27,7 @@ from feast.infra.infra_object import Infra from feast.infra.offline_stores.offline_store import RetrievalJob from feast.infra.registry.base_registry import BaseRegistry +from feast.infra.supported_async_methods import ProviderAsyncMethods from feast.on_demand_feature_view import OnDemandFeatureView from feast.online_response import OnlineResponse from feast.protos.feast.core.Registry_pb2 import Registry as RegistryProto @@ -55,6 +56,10 @@ class Provider(ABC): def __init__(self, config: RepoConfig): pass + @property + def async_supported(self) -> ProviderAsyncMethods: + return ProviderAsyncMethods() + @abstractmethod def update_infra( self, diff --git a/sdk/python/feast/infra/supported_async_methods.py b/sdk/python/feast/infra/supported_async_methods.py new file mode 100644 index 00000000000..b675aa70406 --- /dev/null +++ b/sdk/python/feast/infra/supported_async_methods.py @@ -0,0 +1,10 @@ +from pydantic import BaseModel, Field + + +class SupportedAsyncMethods(BaseModel): + read: bool = Field(default=False) + write: bool = Field(default=False) + + +class ProviderAsyncMethods(BaseModel): + online: SupportedAsyncMethods = Field(default_factory=SupportedAsyncMethods) diff --git a/sdk/python/feast/permissions/matcher.py b/sdk/python/feast/permissions/matcher.py index 337bfd5c57d..5cb0de85e32 100644 --- a/sdk/python/feast/permissions/matcher.py +++ b/sdk/python/feast/permissions/matcher.py @@ -44,7 +44,7 @@ def _get_type(resource: "FeastObject") -> Any: def resource_match_config( resource: "FeastObject", expected_types: list["FeastObject"], - name_pattern: Optional[str] = None, + name_patterns: list[str], required_tags: Optional[dict[str, str]] = None, ) -> bool: """ @@ -53,7 +53,7 @@ def resource_match_config( Args: resource: A FeastObject instance to match agains the permission. expected_types: The list of object types configured in the permission. Type match also includes all the sub-classes. - name_pattern: The optional name pattern filter configured in the permission. + name_patterns: The possibly empty list of name pattern filters configured in the permission. required_tags: The optional dictionary of required tags configured in the permission. Returns: @@ -75,21 +75,8 @@ def resource_match_config( ) return False - if name_pattern is not None: - if hasattr(resource, "name"): - if isinstance(resource.name, str): - match = bool(re.fullmatch(name_pattern, resource.name)) - if not match: - logger.info( - f"Resource name {resource.name} does not match pattern {name_pattern}" - ) - return False - else: - logger.warning( - f"Resource {resource} has no `name` attribute of unexpected type {type(resource.name)}" - ) - else: - logger.warning(f"Resource {resource} has no `name` attribute") + if not _resource_name_matches_name_patterns(resource, name_patterns): + return False if required_tags: if hasattr(resource, "required_tags"): @@ -112,6 +99,39 @@ def resource_match_config( return True +def _resource_name_matches_name_patterns( + resource: "FeastObject", + name_patterns: list[str], +) -> bool: + if not hasattr(resource, "name"): + logger.warning(f"Resource {resource} has no `name` attribute") + return True + + if not name_patterns: + return True + + if resource.name is None: + return True + + if not isinstance(resource.name, str): + logger.warning( + f"Resource {resource} has `name` attribute of unexpected type {type(resource.name)}" + ) + return True + + for name_pattern in name_patterns: + match = bool(re.fullmatch(name_pattern, resource.name)) + if not match: + logger.info( + f"Resource name {resource.name} does not match pattern {name_pattern}" + ) + else: + logger.info(f"Resource name {resource.name} matched pattern {name_pattern}") + return True + + return False + + def actions_match_config( requested_actions: list[AuthzedAction], allowed_actions: list[AuthzedAction], diff --git a/sdk/python/feast/permissions/permission.py b/sdk/python/feast/permissions/permission.py index 9046abbfa9d..964ca743e71 100644 --- a/sdk/python/feast/permissions/permission.py +++ b/sdk/python/feast/permissions/permission.py @@ -33,7 +33,7 @@ class Permission(ABC): name: The permission name (can be duplicated, used for logging troubleshooting). types: The list of protected resource types as defined by the `FeastObject` type. The match includes all the sub-classes of the given types. Defaults to all managed types (e.g. the `ALL_RESOURCE_TYPES` constant) - name_pattern: A regex to match the resource name. Defaults to None, meaning that no name filtering is applied + name_patterns: A possibly empty list of regex patterns to match the resource name. Defaults to empty list, e.g. no name filtering is applied be present in a resource tags with the given value. Defaults to None, meaning that no tags filtering is applied. actions: The actions authorized by this permission. Defaults to `ALL_ACTIONS`. policy: The policy to be applied to validate a client request. @@ -43,7 +43,7 @@ class Permission(ABC): _name: str _types: list["FeastObject"] - _name_pattern: Optional[str] + _name_patterns: list[str] _actions: list[AuthzedAction] _policy: Policy _tags: Dict[str, str] @@ -54,8 +54,8 @@ class Permission(ABC): def __init__( self, name: str, - types: Optional[Union[list["FeastObject"], "FeastObject"]] = None, - name_pattern: Optional[str] = None, + types: Optional[Union[list["FeastObject"], "FeastObject"]] = [], + name_patterns: Optional[Union[str, list[str]]] = [], actions: Union[list[AuthzedAction], AuthzedAction] = ALL_ACTIONS, policy: Policy = AllowAll, tags: Optional[dict[str, str]] = None, @@ -74,7 +74,7 @@ def __init__( raise ValueError("The list 'policy' must be non-empty.") self._name = name self._types = types if isinstance(types, list) else [types] - self._name_pattern = _normalize_name_pattern(name_pattern) + self._name_patterns = _normalize_name_patterns(name_patterns) self._actions = actions if isinstance(actions, list) else [actions] self._policy = policy self._tags = _normalize_tags(tags) @@ -88,7 +88,7 @@ def __eq__(self, other): if ( self.name != other.name - or self.name_pattern != other.name_pattern + or self.name_patterns != other.name_patterns or self.tags != other.tags or self.policy != other.policy or self.actions != other.actions @@ -116,8 +116,8 @@ def types(self) -> list["FeastObject"]: return self._types @property - def name_pattern(self) -> Optional[str]: - return self._name_pattern + def name_patterns(self) -> list[str]: + return self._name_patterns @property def actions(self) -> list[AuthzedAction]: @@ -143,7 +143,7 @@ def match_resource(self, resource: "FeastObject") -> bool: return resource_match_config( resource=resource, expected_types=self.types, - name_pattern=self.name_pattern, + name_patterns=self.name_patterns, required_tags=self.required_tags, ) @@ -175,6 +175,9 @@ def from_proto(permission_proto: PermissionProto) -> Any: ) for t in permission_proto.spec.types ] + name_patterns = [ + name_pattern for name_pattern in permission_proto.spec.name_patterns + ] actions = [ AuthzedAction[PermissionSpecProto.AuthzedAction.Name(action)] for action in permission_proto.spec.actions @@ -183,7 +186,7 @@ def from_proto(permission_proto: PermissionProto) -> Any: permission = Permission( permission_proto.spec.name, types, - permission_proto.spec.name_pattern or None, + name_patterns, actions, Policy.from_proto(permission_proto.spec.policy), dict(permission_proto.spec.tags) or None, @@ -220,7 +223,7 @@ def to_proto(self) -> PermissionProto: permission_spec = PermissionSpecProto( name=self.name, types=types, - name_pattern=self.name_pattern if self.name_pattern is not None else "", + name_patterns=self.name_patterns, actions=actions, policy=self.policy.to_proto(), tags=self.tags, @@ -236,10 +239,17 @@ def to_proto(self) -> PermissionProto: return PermissionProto(spec=permission_spec, meta=meta) -def _normalize_name_pattern(name_pattern: Optional[str]): - if name_pattern is not None: - return name_pattern.strip() - return None +def _normalize_name_patterns( + name_patterns: Optional[Union[str, list[str]]], +) -> list[str]: + if name_patterns is None: + return [] + if isinstance(name_patterns, str): + return _normalize_name_patterns([name_patterns]) + normalized_name_patterns = [] + for name_pattern in name_patterns: + normalized_name_patterns.append(name_pattern.strip()) + return normalized_name_patterns def _normalize_tags(tags: Optional[dict[str, str]]): diff --git a/sdk/python/feast/protos/feast/core/Permission_pb2.py b/sdk/python/feast/protos/feast/core/Permission_pb2.py index 822ad0261b0..706fd2eec47 100644 --- a/sdk/python/feast/protos/feast/core/Permission_pb2.py +++ b/sdk/python/feast/protos/feast/core/Permission_pb2.py @@ -16,7 +16,7 @@ from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x66\x65\x61st/core/Permission.proto\x12\nfeast.core\x1a\x17\x66\x65\x61st/core/Policy.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"`\n\nPermission\x12(\n\x04spec\x18\x01 \x01(\x0b\x32\x1a.feast.core.PermissionSpec\x12(\n\x04meta\x18\x02 \x01(\x0b\x32\x1a.feast.core.PermissionMeta\"\x9f\x06\n\x0ePermissionSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12.\n\x05types\x18\x03 \x03(\x0e\x32\x1f.feast.core.PermissionSpec.Type\x12\x14\n\x0cname_pattern\x18\x04 \x01(\t\x12\x43\n\rrequired_tags\x18\x05 \x03(\x0b\x32,.feast.core.PermissionSpec.RequiredTagsEntry\x12\x39\n\x07\x61\x63tions\x18\x06 \x03(\x0e\x32(.feast.core.PermissionSpec.AuthzedAction\x12\"\n\x06policy\x18\x07 \x01(\x0b\x32\x12.feast.core.Policy\x12\x32\n\x04tags\x18\x08 \x03(\x0b\x32$.feast.core.PermissionSpec.TagsEntry\x1a\x33\n\x11RequiredTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x89\x01\n\rAuthzedAction\x12\n\n\x06\x43REATE\x10\x00\x12\x0c\n\x08\x44\x45SCRIBE\x10\x01\x12\n\n\x06UPDATE\x10\x02\x12\n\n\x06\x44\x45LETE\x10\x03\x12\x0f\n\x0bREAD_ONLINE\x10\x04\x12\x10\n\x0cREAD_OFFLINE\x10\x05\x12\x10\n\x0cWRITE_ONLINE\x10\x06\x12\x11\n\rWRITE_OFFLINE\x10\x07\"\xe1\x01\n\x04Type\x12\x10\n\x0c\x46\x45\x41TURE_VIEW\x10\x00\x12\x1a\n\x16ON_DEMAND_FEATURE_VIEW\x10\x01\x12\x16\n\x12\x42\x41TCH_FEATURE_VIEW\x10\x02\x12\x17\n\x13STREAM_FEATURE_VIEW\x10\x03\x12\n\n\x06\x45NTITY\x10\x04\x12\x13\n\x0f\x46\x45\x41TURE_SERVICE\x10\x05\x12\x0f\n\x0b\x44\x41TA_SOURCE\x10\x06\x12\x18\n\x14VALIDATION_REFERENCE\x10\x07\x12\x11\n\rSAVED_DATASET\x10\x08\x12\x0e\n\nPERMISSION\x10\t\x12\x0b\n\x07PROJECT\x10\n\"\x83\x01\n\x0ePermissionMeta\x12\x35\n\x11\x63reated_timestamp\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12:\n\x16last_updated_timestamp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.TimestampBT\n\x10\x66\x65\x61st.proto.coreB\x0fPermissionProtoZ/github.com/feast-dev/feast/go/protos/feast/coreb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x66\x65\x61st/core/Permission.proto\x12\nfeast.core\x1a\x17\x66\x65\x61st/core/Policy.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"`\n\nPermission\x12(\n\x04spec\x18\x01 \x01(\x0b\x32\x1a.feast.core.PermissionSpec\x12(\n\x04meta\x18\x02 \x01(\x0b\x32\x1a.feast.core.PermissionMeta\"\xa0\x06\n\x0ePermissionSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12.\n\x05types\x18\x03 \x03(\x0e\x32\x1f.feast.core.PermissionSpec.Type\x12\x15\n\rname_patterns\x18\x04 \x03(\t\x12\x43\n\rrequired_tags\x18\x05 \x03(\x0b\x32,.feast.core.PermissionSpec.RequiredTagsEntry\x12\x39\n\x07\x61\x63tions\x18\x06 \x03(\x0e\x32(.feast.core.PermissionSpec.AuthzedAction\x12\"\n\x06policy\x18\x07 \x01(\x0b\x32\x12.feast.core.Policy\x12\x32\n\x04tags\x18\x08 \x03(\x0b\x32$.feast.core.PermissionSpec.TagsEntry\x1a\x33\n\x11RequiredTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x89\x01\n\rAuthzedAction\x12\n\n\x06\x43REATE\x10\x00\x12\x0c\n\x08\x44\x45SCRIBE\x10\x01\x12\n\n\x06UPDATE\x10\x02\x12\n\n\x06\x44\x45LETE\x10\x03\x12\x0f\n\x0bREAD_ONLINE\x10\x04\x12\x10\n\x0cREAD_OFFLINE\x10\x05\x12\x10\n\x0cWRITE_ONLINE\x10\x06\x12\x11\n\rWRITE_OFFLINE\x10\x07\"\xe1\x01\n\x04Type\x12\x10\n\x0c\x46\x45\x41TURE_VIEW\x10\x00\x12\x1a\n\x16ON_DEMAND_FEATURE_VIEW\x10\x01\x12\x16\n\x12\x42\x41TCH_FEATURE_VIEW\x10\x02\x12\x17\n\x13STREAM_FEATURE_VIEW\x10\x03\x12\n\n\x06\x45NTITY\x10\x04\x12\x13\n\x0f\x46\x45\x41TURE_SERVICE\x10\x05\x12\x0f\n\x0b\x44\x41TA_SOURCE\x10\x06\x12\x18\n\x14VALIDATION_REFERENCE\x10\x07\x12\x11\n\rSAVED_DATASET\x10\x08\x12\x0e\n\nPERMISSION\x10\t\x12\x0b\n\x07PROJECT\x10\n\"\x83\x01\n\x0ePermissionMeta\x12\x35\n\x11\x63reated_timestamp\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12:\n\x16last_updated_timestamp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.TimestampBT\n\x10\x66\x65\x61st.proto.coreB\x0fPermissionProtoZ/github.com/feast-dev/feast/go/protos/feast/coreb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -31,15 +31,15 @@ _globals['_PERMISSION']._serialized_start=101 _globals['_PERMISSION']._serialized_end=197 _globals['_PERMISSIONSPEC']._serialized_start=200 - _globals['_PERMISSIONSPEC']._serialized_end=999 - _globals['_PERMISSIONSPEC_REQUIREDTAGSENTRY']._serialized_start=535 - _globals['_PERMISSIONSPEC_REQUIREDTAGSENTRY']._serialized_end=586 - _globals['_PERMISSIONSPEC_TAGSENTRY']._serialized_start=588 - _globals['_PERMISSIONSPEC_TAGSENTRY']._serialized_end=631 - _globals['_PERMISSIONSPEC_AUTHZEDACTION']._serialized_start=634 - _globals['_PERMISSIONSPEC_AUTHZEDACTION']._serialized_end=771 - _globals['_PERMISSIONSPEC_TYPE']._serialized_start=774 - _globals['_PERMISSIONSPEC_TYPE']._serialized_end=999 - _globals['_PERMISSIONMETA']._serialized_start=1002 - _globals['_PERMISSIONMETA']._serialized_end=1133 + _globals['_PERMISSIONSPEC']._serialized_end=1000 + _globals['_PERMISSIONSPEC_REQUIREDTAGSENTRY']._serialized_start=536 + _globals['_PERMISSIONSPEC_REQUIREDTAGSENTRY']._serialized_end=587 + _globals['_PERMISSIONSPEC_TAGSENTRY']._serialized_start=589 + _globals['_PERMISSIONSPEC_TAGSENTRY']._serialized_end=632 + _globals['_PERMISSIONSPEC_AUTHZEDACTION']._serialized_start=635 + _globals['_PERMISSIONSPEC_AUTHZEDACTION']._serialized_end=772 + _globals['_PERMISSIONSPEC_TYPE']._serialized_start=775 + _globals['_PERMISSIONSPEC_TYPE']._serialized_end=1000 + _globals['_PERMISSIONMETA']._serialized_start=1003 + _globals['_PERMISSIONMETA']._serialized_end=1134 # @@protoc_insertion_point(module_scope) diff --git a/sdk/python/feast/protos/feast/core/Permission_pb2.pyi b/sdk/python/feast/protos/feast/core/Permission_pb2.pyi index 1155c131881..b2387d29465 100644 --- a/sdk/python/feast/protos/feast/core/Permission_pb2.pyi +++ b/sdk/python/feast/protos/feast/core/Permission_pb2.pyi @@ -134,7 +134,7 @@ class PermissionSpec(google.protobuf.message.Message): NAME_FIELD_NUMBER: builtins.int PROJECT_FIELD_NUMBER: builtins.int TYPES_FIELD_NUMBER: builtins.int - NAME_PATTERN_FIELD_NUMBER: builtins.int + NAME_PATTERNS_FIELD_NUMBER: builtins.int REQUIRED_TAGS_FIELD_NUMBER: builtins.int ACTIONS_FIELD_NUMBER: builtins.int POLICY_FIELD_NUMBER: builtins.int @@ -145,7 +145,8 @@ class PermissionSpec(google.protobuf.message.Message): """Name of Feast project.""" @property def types(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___PermissionSpec.Type.ValueType]: ... - name_pattern: builtins.str + @property + def name_patterns(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... @property def required_tags(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: ... @property @@ -163,14 +164,14 @@ class PermissionSpec(google.protobuf.message.Message): name: builtins.str = ..., project: builtins.str = ..., types: collections.abc.Iterable[global___PermissionSpec.Type.ValueType] | None = ..., - name_pattern: builtins.str = ..., + name_patterns: collections.abc.Iterable[builtins.str] | None = ..., required_tags: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., actions: collections.abc.Iterable[global___PermissionSpec.AuthzedAction.ValueType] | None = ..., policy: feast.core.Policy_pb2.Policy | None = ..., tags: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., ) -> None: ... def HasField(self, field_name: typing_extensions.Literal["policy", b"policy"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["actions", b"actions", "name", b"name", "name_pattern", b"name_pattern", "policy", b"policy", "project", b"project", "required_tags", b"required_tags", "tags", b"tags", "types", b"types"]) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["actions", b"actions", "name", b"name", "name_patterns", b"name_patterns", "policy", b"policy", "project", b"project", "required_tags", b"required_tags", "tags", b"tags", "types", b"types"]) -> None: ... global___PermissionSpec = PermissionSpec diff --git a/sdk/python/feast/repo_config.py b/sdk/python/feast/repo_config.py index 1b991d058b8..0a5b484e8c7 100644 --- a/sdk/python/feast/repo_config.py +++ b/sdk/python/feast/repo_config.py @@ -193,6 +193,7 @@ class RepoConfig(FeastBaseModel): """ Flags (deprecated field): Feature flags for experimental features """ repo_path: Optional[Path] = None + """When using relative path in FileSource path, this parameter is mandatory""" entity_key_serialization_version: StrictInt = 1 """ Entity key serialization version: This version is used to control what serialization scheme is diff --git a/sdk/python/feast/templates/cassandra/bootstrap.py b/sdk/python/feast/templates/cassandra/bootstrap.py index fa70917914f..33385141145 100644 --- a/sdk/python/feast/templates/cassandra/bootstrap.py +++ b/sdk/python/feast/templates/cassandra/bootstrap.py @@ -275,7 +275,9 @@ def bootstrap(): # example_repo.py example_py_file = repo_path / "example_repo.py" - replace_str_in_file(example_py_file, "%PARQUET_PATH%", str(driver_stats_path)) + replace_str_in_file( + example_py_file, "%PARQUET_PATH%", str(driver_stats_path.relative_to(repo_path)) + ) # store config yaml, interact with user and then customize file: settings = collect_cassandra_store_settings() diff --git a/sdk/python/feast/templates/hazelcast/bootstrap.py b/sdk/python/feast/templates/hazelcast/bootstrap.py index e5018e4fe02..7a2b49d2493 100644 --- a/sdk/python/feast/templates/hazelcast/bootstrap.py +++ b/sdk/python/feast/templates/hazelcast/bootstrap.py @@ -165,7 +165,9 @@ def bootstrap(): # example_repo.py example_py_file = repo_path / "example_repo.py" - replace_str_in_file(example_py_file, "%PARQUET_PATH%", str(driver_stats_path)) + replace_str_in_file( + example_py_file, "%PARQUET_PATH%", str(driver_stats_path.relative_to(repo_path)) + ) # store config yaml, interact with user and then customize file: settings = collect_hazelcast_online_store_settings() diff --git a/sdk/python/feast/templates/hbase/bootstrap.py b/sdk/python/feast/templates/hbase/bootstrap.py index 125eb7c2e72..94be8e441da 100644 --- a/sdk/python/feast/templates/hbase/bootstrap.py +++ b/sdk/python/feast/templates/hbase/bootstrap.py @@ -23,7 +23,9 @@ def bootstrap(): driver_df.to_parquet(path=str(driver_stats_path), allow_truncated_timestamps=True) example_py_file = repo_path / "example_repo.py" - replace_str_in_file(example_py_file, "%PARQUET_PATH%", str(driver_stats_path)) + replace_str_in_file( + example_py_file, "%PARQUET_PATH%", str(driver_stats_path.relative_to(repo_path)) + ) if __name__ == "__main__": diff --git a/sdk/python/feast/templates/local/bootstrap.py b/sdk/python/feast/templates/local/bootstrap.py index e2c1efdbc49..9f6a5a6c969 100644 --- a/sdk/python/feast/templates/local/bootstrap.py +++ b/sdk/python/feast/templates/local/bootstrap.py @@ -25,8 +25,12 @@ def bootstrap(): example_py_file = repo_path / "example_repo.py" replace_str_in_file(example_py_file, "%PROJECT_NAME%", str(project_name)) - replace_str_in_file(example_py_file, "%PARQUET_PATH%", str(driver_stats_path)) - replace_str_in_file(example_py_file, "%LOGGING_PATH%", str(data_path)) + replace_str_in_file( + example_py_file, "%PARQUET_PATH%", str(driver_stats_path.relative_to(repo_path)) + ) + replace_str_in_file( + example_py_file, "%LOGGING_PATH%", str(data_path.relative_to(repo_path)) + ) if __name__ == "__main__": diff --git a/sdk/python/requirements/py3.10-ci-requirements.txt b/sdk/python/requirements/py3.10-ci-requirements.txt index 6268de6ae13..be13d71b829 100644 --- a/sdk/python/requirements/py3.10-ci-requirements.txt +++ b/sdk/python/requirements/py3.10-ci-requirements.txt @@ -767,7 +767,7 @@ sqlparams==6.1.0 # via singlestoredb stack-data==0.6.3 # via ipython -starlette==0.38.5 +starlette==0.40.0 # via fastapi substrait==0.23.0 # via ibis-substrait diff --git a/sdk/python/requirements/py3.10-requirements.txt b/sdk/python/requirements/py3.10-requirements.txt index 9e5eb0be728..8d34dcdcf33 100644 --- a/sdk/python/requirements/py3.10-requirements.txt +++ b/sdk/python/requirements/py3.10-requirements.txt @@ -108,7 +108,7 @@ six==1.16.0 sniffio==1.3.1 # via anyio sqlalchemy[mypy]==2.0.35 -starlette==0.38.5 +starlette==0.40.0 # via fastapi tabulate==0.9.0 tenacity==8.5.0 diff --git a/sdk/python/requirements/py3.11-ci-requirements.txt b/sdk/python/requirements/py3.11-ci-requirements.txt index 946d4e05195..1c0d09139a0 100644 --- a/sdk/python/requirements/py3.11-ci-requirements.txt +++ b/sdk/python/requirements/py3.11-ci-requirements.txt @@ -758,7 +758,7 @@ sqlparams==6.1.0 # via singlestoredb stack-data==0.6.3 # via ipython -starlette==0.38.5 +starlette==0.40.0 # via fastapi substrait==0.23.0 # via ibis-substrait diff --git a/sdk/python/requirements/py3.11-requirements.txt b/sdk/python/requirements/py3.11-requirements.txt index 1ce25e7d5b7..649b08f492a 100644 --- a/sdk/python/requirements/py3.11-requirements.txt +++ b/sdk/python/requirements/py3.11-requirements.txt @@ -106,7 +106,7 @@ six==1.16.0 sniffio==1.3.1 # via anyio sqlalchemy[mypy]==2.0.35 -starlette==0.38.5 +starlette==0.40.0 # via fastapi tabulate==0.9.0 tenacity==8.5.0 diff --git a/sdk/python/requirements/py3.9-ci-requirements.txt b/sdk/python/requirements/py3.9-ci-requirements.txt index 5ea2c58819d..3dba480af68 100644 --- a/sdk/python/requirements/py3.9-ci-requirements.txt +++ b/sdk/python/requirements/py3.9-ci-requirements.txt @@ -775,7 +775,7 @@ sqlparams==6.1.0 # via singlestoredb stack-data==0.6.3 # via ipython -starlette==0.38.5 +starlette==0.40.0 # via fastapi substrait==0.23.0 # via ibis-substrait diff --git a/sdk/python/requirements/py3.9-requirements.txt b/sdk/python/requirements/py3.9-requirements.txt index 857d7d72bf7..ba30a4ecf52 100644 --- a/sdk/python/requirements/py3.9-requirements.txt +++ b/sdk/python/requirements/py3.9-requirements.txt @@ -110,7 +110,7 @@ six==1.16.0 sniffio==1.3.1 # via anyio sqlalchemy[mypy]==2.0.35 -starlette==0.38.5 +starlette==0.40.0 # via fastapi tabulate==0.9.0 tenacity==8.5.0 diff --git a/sdk/python/tests/doctest/test_all.py b/sdk/python/tests/doctest/test_all.py index 52348e7da4e..d1b2161252f 100644 --- a/sdk/python/tests/doctest/test_all.py +++ b/sdk/python/tests/doctest/test_all.py @@ -26,7 +26,7 @@ def setup_feature_store(): description="driver id", ) driver_hourly_stats = FileSource( - path="project/feature_repo/data/driver_stats.parquet", + path="data/driver_stats.parquet", timestamp_field="event_timestamp", created_timestamp_column="created", ) diff --git a/sdk/python/tests/integration/registration/test_universal_registry.py b/sdk/python/tests/integration/registration/test_universal_registry.py index 0bed89ca162..a194b8ae26b 100644 --- a/sdk/python/tests/integration/registration/test_universal_registry.py +++ b/sdk/python/tests/integration/registration/test_universal_registry.py @@ -1463,7 +1463,7 @@ def test_apply_permission_success(test_registry): and isinstance(permission.policy, RoleBasedPolicy) and len(permission.policy.roles) == 1 and permission.policy.roles[0] == "reader" - and permission.name_pattern is None + and permission.name_patterns == [] and permission.tags is None and permission.required_tags is None ) @@ -1481,7 +1481,7 @@ def test_apply_permission_success(test_registry): and isinstance(permission.policy, RoleBasedPolicy) and len(permission.policy.roles) == 1 and permission.policy.roles[0] == "reader" - and permission.name_pattern is None + and permission.name_patterns == [] and permission.tags is None and permission.required_tags is None ) @@ -1511,7 +1511,7 @@ def test_apply_permission_success(test_registry): and len(updated_permission.policy.roles) == 2 and "reader" in updated_permission.policy.roles and "writer" in updated_permission.policy.roles - and updated_permission.name_pattern is None + and updated_permission.name_patterns == [] and updated_permission.tags is None and updated_permission.required_tags is None ) @@ -1527,7 +1527,7 @@ def test_apply_permission_success(test_registry): actions=[AuthzedAction.DESCRIBE, AuthzedAction.WRITE_ONLINE], policy=RoleBasedPolicy(roles=["reader", "writer"]), types=FeatureView, - name_pattern="aaa", + name_patterns="aaa", tags={"team": "matchmaking"}, required_tags={"tag1": "tag1-value"}, ) @@ -1549,7 +1549,7 @@ def test_apply_permission_success(test_registry): and len(updated_permission.policy.roles) == 2 and "reader" in updated_permission.policy.roles and "writer" in updated_permission.policy.roles - and updated_permission.name_pattern == "aaa" + and updated_permission.name_patterns == ["aaa"] and "team" in updated_permission.tags and updated_permission.tags["team"] == "matchmaking" and updated_permission.required_tags["tag1"] == "tag1-value" diff --git a/sdk/python/tests/unit/infra/offline_stores/test_offline_store.py b/sdk/python/tests/unit/infra/offline_stores/test_offline_store.py index 6d5eeb90c71..afc0e4e5c8f 100644 --- a/sdk/python/tests/unit/infra/offline_stores/test_offline_store.py +++ b/sdk/python/tests/unit/infra/offline_stores/test_offline_store.py @@ -109,7 +109,7 @@ def metadata(self) -> Optional[RetrievalMetadata]: ) def retrieval_job(request, environment): if request.param is DaskRetrievalJob: - return DaskRetrievalJob(lambda: 1, full_feature_names=False) + return DaskRetrievalJob(lambda: 1, full_feature_names=False, repo_path="") elif request.param is RedshiftRetrievalJob: offline_store_config = RedshiftOfflineStoreConfig( cluster_id="feast-int-bucket", diff --git a/sdk/python/tests/unit/permissions/conftest.py b/sdk/python/tests/unit/permissions/conftest.py index 6adbc6ec54b..ba277d13b49 100644 --- a/sdk/python/tests/unit/permissions/conftest.py +++ b/sdk/python/tests/unit/permissions/conftest.py @@ -79,7 +79,7 @@ def security_manager() -> SecurityManager: Permission( name="special", types=FeatureView, - name_pattern="special.*", + name_patterns="special.*", policy=RoleBasedPolicy(roles=["special-reader"]), actions=[AuthzedAction.DESCRIBE, AuthzedAction.UPDATE], ) diff --git a/sdk/python/tests/unit/permissions/test_permission.py b/sdk/python/tests/unit/permissions/test_permission.py index 606d750d813..8f1f2c46ba6 100644 --- a/sdk/python/tests/unit/permissions/test_permission.py +++ b/sdk/python/tests/unit/permissions/test_permission.py @@ -11,9 +11,8 @@ from feast.feature_view import FeatureView from feast.on_demand_feature_view import OnDemandFeatureView from feast.permissions.action import ALL_ACTIONS, AuthzedAction -from feast.permissions.permission import ( - Permission, -) +from feast.permissions.matcher import _resource_name_matches_name_patterns +from feast.permissions.permission import Permission, _normalize_name_patterns from feast.permissions.policy import AllowAll, Policy from feast.saved_dataset import ValidationReference from feast.stream_feature_view import StreamFeatureView @@ -23,7 +22,7 @@ def test_defaults(): p = Permission(name="test") assertpy.assert_that(type(p.types)).is_equal_to(list) assertpy.assert_that(p.types).is_equal_to(ALL_RESOURCE_TYPES) - assertpy.assert_that(p.name_pattern).is_none() + assertpy.assert_that(p.name_patterns).is_equal_to([]) assertpy.assert_that(p.tags).is_none() assertpy.assert_that(type(p.actions)).is_equal_to(list) assertpy.assert_that(p.actions).is_equal_to(ALL_ACTIONS) @@ -66,6 +65,53 @@ def test_normalized_args(): assertpy.assert_that(type(p.actions)).is_equal_to(list) assertpy.assert_that(p.actions).is_equal_to([AuthzedAction.CREATE]) + p = Permission(name="test", name_patterns=None) + assertpy.assert_that(type(p.name_patterns)).is_equal_to(list) + assertpy.assert_that(p.name_patterns).is_equal_to([]) + + p = Permission(name="test", name_patterns="a_pattern") + assertpy.assert_that(type(p.name_patterns)).is_equal_to(list) + assertpy.assert_that(p.name_patterns).is_equal_to(["a_pattern"]) + + p = Permission(name="test", name_patterns=["pattern1", "pattern2"]) + assertpy.assert_that(type(p.name_patterns)).is_equal_to(list) + assertpy.assert_that(p.name_patterns).is_equal_to(["pattern1", "pattern2"]) + + p = Permission( + name="test", name_patterns=[" pattern1 ", " pattern2", "pattern3 "] + ) + assertpy.assert_that(type(p.name_patterns)).is_equal_to(list) + assertpy.assert_that(p.name_patterns).is_equal_to( + ["pattern1", "pattern2", "pattern3"] + ) + + +@pytest.mark.parametrize( + "name, patterns, result", + [ + (None, None, True), + (None, "", True), + (None, [], True), + (None, [""], True), + ("name", "name", True), + ("name", "another", False), + ("name", ".*me", True), + ("name", "^na.*", True), + ("123_must_start_by_number", r"^[\d].*", True), + ("name", ["invalid", "another_invalid"], False), + ("name", ["invalid", "name"], True), + ("name", ["name", "invalid"], True), + ("name", ["invalid", "another_invalid", "name"], True), + ("name", ["invalid", "name", "name"], True), + ], +) +def test_match_name_patterns(name, patterns, result): + assertpy.assert_that( + _resource_name_matches_name_patterns( + Permission(name=name), _normalize_name_patterns(patterns) + ) + ).is_equal_to(result) + @pytest.mark.parametrize( "resource, types, result", @@ -152,7 +198,7 @@ def test_match_resource_with_subclasses(resource, types, result): ], ) def test_resource_match_with_name_filter(pattern, name, match): - p = Permission(name="test", name_pattern=pattern) + p = Permission(name="test", name_patterns=pattern) for t in ALL_RESOURCE_TYPES: resource = Mock(spec=t) resource.name = name diff --git a/sdk/python/tests/unit/test_offline_server.py b/sdk/python/tests/unit/test_offline_server.py index 7c38d9bfca4..e82e2fa6872 100644 --- a/sdk/python/tests/unit/test_offline_server.py +++ b/sdk/python/tests/unit/test_offline_server.py @@ -95,6 +95,7 @@ def remote_feature_store(offline_server): provider="local", offline_store=offline_config, entity_key_serialization_version=2, + # repo_config = ) ) return store diff --git a/sdk/python/tests/utils/auth_permissions_util.py b/sdk/python/tests/utils/auth_permissions_util.py index 3b5e589812a..b8ca7355e98 100644 --- a/sdk/python/tests/utils/auth_permissions_util.py +++ b/sdk/python/tests/utils/auth_permissions_util.py @@ -119,6 +119,7 @@ def get_remote_registry_store(server_port, feature_store): registry=registry_config, provider="local", entity_key_serialization_version=2, + repo_path=feature_store.repo_path, ) ) return store diff --git a/ui/package.json b/ui/package.json index a79b039a870..09c16f25716 100644 --- a/ui/package.json +++ b/ui/package.json @@ -27,7 +27,6 @@ "@elastic/eui": "^95.12.0", "@emotion/css": "^11.13.0", "@emotion/react": "^11.13.3", - "d3": "^7.3.0", "inter-ui": "^3.19.3", "moment": "^2.29.1", "protobufjs": "^7.1.1", @@ -57,8 +56,7 @@ }, "jest": { "moduleNameMapper": { - "chroma-js": "/node_modules/chroma-js/dist/chroma.min.cjs", - "d3": "/node_modules/d3/dist/d3.min.js" + "chroma-js": "/node_modules/chroma-js/dist/chroma.min.cjs" } }, "browserslist": { @@ -86,7 +84,6 @@ "@testing-library/jest-dom": "^6.5.0", "@testing-library/react": "^16.0.1", "@testing-library/user-event": "^14.5.2", - "@types/d3": "^7.1.0", "@types/jest": "^27.0.1", "@types/node": "^16.7.13", "@types/react": "^18.3.11", diff --git a/ui/yarn.lock b/ui/yarn.lock index 27cc4076a33..894398942c4 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -2247,216 +2247,6 @@ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== -"@types/d3-array@*": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.0.2.tgz#71c35bca8366a40d1b8fce9279afa4a77fb0065d" - integrity sha512-5mjGjz6XOXKOCdTajXTZ/pMsg236RdiwKPrRPWAEf/2S/+PzwY+LLYShUpeysWaMvsdS7LArh6GdUefoxpchsQ== - -"@types/d3-axis@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-3.0.1.tgz#6afc20744fa5cc0cbc3e2bd367b140a79ed3e7a8" - integrity sha512-zji/iIbdd49g9WN0aIsGcwcTBUkgLsCSwB+uH+LPVDAiKWENMtI3cJEWt+7/YYwelMoZmbBfzA3qCdrZ2XFNnw== - dependencies: - "@types/d3-selection" "*" - -"@types/d3-brush@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-3.0.1.tgz#ae5f17ce391935ca88b29000e60ee20452c6357c" - integrity sha512-B532DozsiTuQMHu2YChdZU0qsFJSio3Q6jmBYGYNp3gMDzBmuFFgPt9qKA4VYuLZMp4qc6eX7IUFUEsvHiXZAw== - dependencies: - "@types/d3-selection" "*" - -"@types/d3-chord@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-3.0.1.tgz#54c8856c19c8e4ab36a53f73ba737de4768ad248" - integrity sha512-eQfcxIHrg7V++W8Qxn6QkqBNBokyhdWSAS73AbkbMzvLQmVVBviknoz2SRS/ZJdIOmhcmmdCRE/NFOm28Z1AMw== - -"@types/d3-color@*": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.0.2.tgz#53f2d6325f66ee79afd707c05ac849e8ae0edbb0" - integrity sha512-WVx6zBiz4sWlboCy7TCgjeyHpNjMsoF36yaagny1uXfbadc9f+5BeBf7U+lRmQqY3EHbGQpP8UdW8AC+cywSwQ== - -"@types/d3-contour@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-contour/-/d3-contour-3.0.1.tgz#9ff4e2fd2a3910de9c5097270a7da8a6ef240017" - integrity sha512-C3zfBrhHZvrpAAK3YXqLWVAGo87A4SvJ83Q/zVJ8rFWJdKejUnDYaWZPkA8K84kb2vDA/g90LTQAz7etXcgoQQ== - dependencies: - "@types/d3-array" "*" - "@types/geojson" "*" - -"@types/d3-delaunay@*": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@types/d3-delaunay/-/d3-delaunay-6.0.0.tgz#c09953ac7e5460997f693d2d7bf3522e0d4a88e6" - integrity sha512-iGm7ZaGLq11RK3e69VeMM6Oqj2SjKUB9Qhcyd1zIcqn2uE8w9GFB445yCY46NOQO3ByaNyktX1DK+Etz7ZaX+w== - -"@types/d3-dispatch@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-3.0.1.tgz#a1b18ae5fa055a6734cb3bd3cbc6260ef19676e3" - integrity sha512-NhxMn3bAkqhjoxabVJWKryhnZXXYYVQxaBnbANu0O94+O/nX9qSjrA1P1jbAQJxJf+VC72TxDX/YJcKue5bRqw== - -"@types/d3-drag@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-3.0.1.tgz#fb1e3d5cceeee4d913caa59dedf55c94cb66e80f" - integrity sha512-o1Va7bLwwk6h03+nSM8dpaGEYnoIG19P0lKqlic8Un36ymh9NSkNFX1yiXMKNMx8rJ0Kfnn2eovuFaL6Jvj0zA== - dependencies: - "@types/d3-selection" "*" - -"@types/d3-dsv@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-3.0.0.tgz#f3c61fb117bd493ec0e814856feb804a14cfc311" - integrity sha512-o0/7RlMl9p5n6FQDptuJVMxDf/7EDEv2SYEO/CwdG2tr1hTfUVi0Iavkk2ax+VpaQ/1jVhpnj5rq1nj8vwhn2A== - -"@types/d3-ease@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.0.tgz#c29926f8b596f9dadaeca062a32a45365681eae0" - integrity sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA== - -"@types/d3-fetch@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-fetch/-/d3-fetch-3.0.1.tgz#f9fa88b81aa2eea5814f11aec82ecfddbd0b8fe0" - integrity sha512-toZJNOwrOIqz7Oh6Q7l2zkaNfXkfR7mFSJvGvlD/Ciq/+SQ39d5gynHJZ/0fjt83ec3WL7+u3ssqIijQtBISsw== - dependencies: - "@types/d3-dsv" "*" - -"@types/d3-force@*": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-3.0.3.tgz#76cb20d04ae798afede1ea6e41750763ff5a9c82" - integrity sha512-z8GteGVfkWJMKsx6hwC3SiTSLspL98VNpmvLpEFJQpZPq6xpA1I8HNBDNSpukfK0Vb0l64zGFhzunLgEAcBWSA== - -"@types/d3-format@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-3.0.1.tgz#194f1317a499edd7e58766f96735bdc0216bb89d" - integrity sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg== - -"@types/d3-geo@*": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-3.0.2.tgz#e7ec5f484c159b2c404c42d260e6d99d99f45d9a" - integrity sha512-DbqK7MLYA8LpyHQfv6Klz0426bQEf7bRTvhMy44sNGVyZoWn//B0c+Qbeg8Osi2Obdc9BLLXYAKpyWege2/7LQ== - dependencies: - "@types/geojson" "*" - -"@types/d3-hierarchy@*": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-3.0.2.tgz#ca63f2f4da15b8f129c5b7dffd71d904cba6aca2" - integrity sha512-+krnrWOZ+aQB6v+E+jEkmkAx9HvsNAD+1LCD0vlBY3t+HwjKnsBFbpVLx6WWzDzCIuiTWdAxXMEnGnVXpB09qQ== - -"@types/d3-interpolate@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz#e7d17fa4a5830ad56fe22ce3b4fac8541a9572dc" - integrity sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw== - dependencies: - "@types/d3-color" "*" - -"@types/d3-path@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.0.0.tgz#939e3a784ae4f80b1fde8098b91af1776ff1312b" - integrity sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg== - -"@types/d3-polygon@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-3.0.0.tgz#5200a3fa793d7736fa104285fa19b0dbc2424b93" - integrity sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw== - -"@types/d3-quadtree@*": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz#433112a178eb7df123aab2ce11c67f51cafe8ff5" - integrity sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw== - -"@types/d3-random@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-3.0.1.tgz#5c8d42b36cd4c80b92e5626a252f994ca6bfc953" - integrity sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ== - -"@types/d3-scale-chromatic@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#103124777e8cdec85b20b51fd3397c682ee1e954" - integrity sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw== - -"@types/d3-scale@*": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.2.tgz#41be241126af4630524ead9cb1008ab2f0f26e69" - integrity sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA== - dependencies: - "@types/d3-time" "*" - -"@types/d3-selection@*": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.2.tgz#23e48a285b24063630bbe312cc0cfe2276de4a59" - integrity sha512-d29EDd0iUBrRoKhPndhDY6U/PYxOWqgIZwKTooy2UkBfU7TNZNpRho0yLWPxlatQrFWk2mnTu71IZQ4+LRgKlQ== - -"@types/d3-shape@*": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.0.2.tgz#4b1ca4ddaac294e76b712429726d40365cd1e8ca" - integrity sha512-5+ButCmIfNX8id5seZ7jKj3igdcxx+S9IDBiT35fQGTLZUfkFgTv+oBH34xgeoWDKpWcMITSzBILWQtBoN5Piw== - dependencies: - "@types/d3-path" "*" - -"@types/d3-time-format@*": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-4.0.0.tgz#ee7b6e798f8deb2d9640675f8811d0253aaa1946" - integrity sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw== - -"@types/d3-time@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.0.tgz#e1ac0f3e9e195135361fa1a1d62f795d87e6e819" - integrity sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg== - -"@types/d3-timer@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.0.tgz#e2505f1c21ec08bda8915238e397fb71d2fc54ce" - integrity sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g== - -"@types/d3-transition@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.1.tgz#c9a96125567173d6163a6985b874f79154f4cc3d" - integrity sha512-Sv4qEI9uq3bnZwlOANvYK853zvpdKEm1yz9rcc8ZTsxvRklcs9Fx4YFuGA3gXoQN/c/1T6QkVNjhaRO/cWj94g== - dependencies: - "@types/d3-selection" "*" - -"@types/d3-zoom@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.1.tgz#4bfc7e29625c4f79df38e2c36de52ec3e9faf826" - integrity sha512-7s5L9TjfqIYQmQQEUcpMAcBOahem7TRoSO/+Gkz02GbMVuULiZzjF2BOdw291dbO2aNon4m2OdFsRGaCq2caLQ== - dependencies: - "@types/d3-interpolate" "*" - "@types/d3-selection" "*" - -"@types/d3@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@types/d3/-/d3-7.1.0.tgz#8f32a7e7f434d8f920c8b1ebdfed55e18c033720" - integrity sha512-gYWvgeGjEl+zmF8c+U1RNIKqe7sfQwIXeLXO5Os72TjDjCEtgpvGBvZ8dXlAuSS1m6B90Y1Uo6Bm36OGR/OtCA== - dependencies: - "@types/d3-array" "*" - "@types/d3-axis" "*" - "@types/d3-brush" "*" - "@types/d3-chord" "*" - "@types/d3-color" "*" - "@types/d3-contour" "*" - "@types/d3-delaunay" "*" - "@types/d3-dispatch" "*" - "@types/d3-drag" "*" - "@types/d3-dsv" "*" - "@types/d3-ease" "*" - "@types/d3-fetch" "*" - "@types/d3-force" "*" - "@types/d3-format" "*" - "@types/d3-geo" "*" - "@types/d3-hierarchy" "*" - "@types/d3-interpolate" "*" - "@types/d3-path" "*" - "@types/d3-polygon" "*" - "@types/d3-quadtree" "*" - "@types/d3-random" "*" - "@types/d3-scale" "*" - "@types/d3-scale-chromatic" "*" - "@types/d3-selection" "*" - "@types/d3-shape" "*" - "@types/d3-time" "*" - "@types/d3-time-format" "*" - "@types/d3-timer" "*" - "@types/d3-transition" "*" - "@types/d3-zoom" "*" - "@types/eslint@^7.28.2": version "7.29.0" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78" @@ -2506,11 +2296,6 @@ dependencies: "@types/node" "*" -"@types/geojson@*": - version "7946.0.8" - resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca" - integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA== - "@types/glob@^7.1.1": version "7.2.0" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" @@ -4057,16 +3842,16 @@ comma-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== -commander@7, commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + commander@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -4466,250 +4251,6 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA== -"d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.1.1.tgz#7797eb53ead6b9083c75a45a681e93fc41bc468c" - integrity sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ== - dependencies: - internmap "1 - 2" - -d3-axis@3: - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-3.0.0.tgz#c42a4a13e8131d637b745fc2973824cfeaf93322" - integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw== - -d3-brush@3: - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c" - integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ== - dependencies: - d3-dispatch "1 - 3" - d3-drag "2 - 3" - d3-interpolate "1 - 3" - d3-selection "3" - d3-transition "3" - -d3-chord@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-3.0.1.tgz#d156d61f485fce8327e6abf339cb41d8cbba6966" - integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g== - dependencies: - d3-path "1 - 3" - -"d3-color@1 - 3", d3-color@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.0.1.tgz#03316e595955d1fcd39d9f3610ad41bb90194d0a" - integrity sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw== - -d3-contour@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-3.0.1.tgz#2c64255d43059599cd0dba8fe4cc3d51ccdd9bbd" - integrity sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ== - dependencies: - d3-array "2 - 3" - -d3-delaunay@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.2.tgz#7fd3717ad0eade2fc9939f4260acfb503f984e92" - integrity sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ== - dependencies: - delaunator "5" - -"d3-dispatch@1 - 3", d3-dispatch@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" - integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== - -"d3-drag@2 - 3", d3-drag@3: - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba" - integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== - dependencies: - d3-dispatch "1 - 3" - d3-selection "3" - -"d3-dsv@1 - 3", d3-dsv@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73" - integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q== - dependencies: - commander "7" - iconv-lite "0.6" - rw "1" - -"d3-ease@1 - 3", d3-ease@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" - integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== - -d3-fetch@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-3.0.1.tgz#83141bff9856a0edb5e38de89cdcfe63d0a60a22" - integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw== - dependencies: - d3-dsv "1 - 3" - -d3-force@3: - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4" - integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg== - dependencies: - d3-dispatch "1 - 3" - d3-quadtree "1 - 3" - d3-timer "1 - 3" - -"d3-format@1 - 3", d3-format@3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" - integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== - -d3-geo@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.0.1.tgz#4f92362fd8685d93e3b1fae0fd97dc8980b1ed7e" - integrity sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA== - dependencies: - d3-array "2.5.0 - 3" - -d3-hierarchy@3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.1.tgz#9cbb0ffd2375137a351e6cfeed344a06d4ff4597" - integrity sha512-LtAIu54UctRmhGKllleflmHalttH3zkfSi4NlKrTAoFKjC+AFBJohsCAdgCBYQwH0F8hIOGY89X1pPqAchlMkA== - -"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" - integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== - dependencies: - d3-color "1 - 3" - -"d3-path@1 - 3", d3-path@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.0.1.tgz#f09dec0aaffd770b7995f1a399152bf93052321e" - integrity sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w== - -d3-polygon@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-3.0.1.tgz#0b45d3dd1c48a29c8e057e6135693ec80bf16398" - integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg== - -"d3-quadtree@1 - 3", d3-quadtree@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f" - integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw== - -d3-random@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4" - integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ== - -d3-scale-chromatic@3: - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#15b4ceb8ca2bb0dcb6d1a641ee03d59c3b62376a" - integrity sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g== - dependencies: - d3-color "1 - 3" - d3-interpolate "1 - 3" - -d3-scale@4: - version "4.0.2" - resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" - integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== - dependencies: - d3-array "2.10.0 - 3" - d3-format "1 - 3" - d3-interpolate "1.2.0 - 3" - d3-time "2.1.1 - 3" - d3-time-format "2 - 4" - -"d3-selection@2 - 3", d3-selection@3: - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" - integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== - -d3-shape@3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.1.0.tgz#c8a495652d83ea6f524e482fca57aa3f8bc32556" - integrity sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ== - dependencies: - d3-path "1 - 3" - -"d3-time-format@2 - 4", d3-time-format@4: - version "4.1.0" - resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" - integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== - dependencies: - d3-time "1 - 3" - -"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3: - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.0.0.tgz#65972cb98ae2d4954ef5c932e8704061335d4975" - integrity sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ== - dependencies: - d3-array "2 - 3" - -"d3-timer@1 - 3", d3-timer@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" - integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== - -"d3-transition@2 - 3", d3-transition@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f" - integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w== - dependencies: - d3-color "1 - 3" - d3-dispatch "1 - 3" - d3-ease "1 - 3" - d3-interpolate "1 - 3" - d3-timer "1 - 3" - -d3-zoom@3: - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3" - integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== - dependencies: - d3-dispatch "1 - 3" - d3-drag "2 - 3" - d3-interpolate "1 - 3" - d3-selection "2 - 3" - d3-transition "2 - 3" - -d3@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/d3/-/d3-7.3.0.tgz#f3d5a22c1f658952a6491cf50132f5267ed7a40a" - integrity sha512-MDRLJCMK232OJQRqGljQ/gCxtB8k3/sLKFjftMjzPB3nKVUODpdW9Rb3vcq7U8Ka5YKoZkAmp++Ur6I+6iNWIw== - dependencies: - d3-array "3" - d3-axis "3" - d3-brush "3" - d3-chord "3" - d3-color "3" - d3-contour "3" - d3-delaunay "6" - d3-dispatch "3" - d3-drag "3" - d3-dsv "3" - d3-ease "3" - d3-fetch "3" - d3-force "3" - d3-format "3" - d3-geo "3" - d3-hierarchy "3" - d3-interpolate "3" - d3-path "3" - d3-polygon "3" - d3-quadtree "3" - d3-random "3" - d3-scale "4" - d3-scale-chromatic "3" - d3-selection "3" - d3-shape "3" - d3-time "3" - d3-time-format "4" - d3-timer "3" - d3-transition "3" - d3-zoom "3" - damerau-levenshtein@^1.0.7: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -4827,13 +4368,6 @@ del@^6.0.0: rimraf "^3.0.2" slash "^3.0.0" -delaunator@5: - version "5.0.0" - resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-5.0.0.tgz#60f052b28bd91c9b4566850ebf7756efe821d81b" - integrity sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw== - dependencies: - robust-predicates "^3.0.0" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -6428,7 +5962,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6, iconv-lite@^0.6.3: +iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -6560,11 +6094,6 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" -"internmap@1 - 2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" - integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== - invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -9912,11 +9441,6 @@ rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -robust-predicates@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a" - integrity sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g== - rollup-plugin-copy@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz#f1228a3ffb66ffad8606e2f3fb7ff23141ed3286" @@ -9986,11 +9510,6 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rw@1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" - integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q= - rxjs@^7.2.0: version "7.5.4" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.4.tgz#3d6bd407e6b7ce9a123e76b1e770dc5761aa368d"