Feature: Personalize (Schemas) (#5398)

This commit is contained in:
Bert Blommers 2022-08-23 18:41:10 +00:00 committed by GitHub
parent dffd817a37
commit c2d07e04b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 424 additions and 1 deletions

View File

@ -4199,6 +4199,72 @@
- [X] update_policy
</details>
## personalize
<details>
<summary>6% implemented</summary>
- [ ] create_batch_inference_job
- [ ] create_batch_segment_job
- [ ] create_campaign
- [ ] create_dataset
- [ ] create_dataset_export_job
- [ ] create_dataset_group
- [ ] create_dataset_import_job
- [ ] create_event_tracker
- [ ] create_filter
- [ ] create_recommender
- [X] create_schema
- [ ] create_solution
- [ ] create_solution_version
- [ ] delete_campaign
- [ ] delete_dataset
- [ ] delete_dataset_group
- [ ] delete_event_tracker
- [ ] delete_filter
- [ ] delete_recommender
- [X] delete_schema
- [ ] delete_solution
- [ ] describe_algorithm
- [ ] describe_batch_inference_job
- [ ] describe_batch_segment_job
- [ ] describe_campaign
- [ ] describe_dataset
- [ ] describe_dataset_export_job
- [ ] describe_dataset_group
- [ ] describe_dataset_import_job
- [ ] describe_event_tracker
- [ ] describe_feature_transformation
- [ ] describe_filter
- [ ] describe_recipe
- [ ] describe_recommender
- [X] describe_schema
- [ ] describe_solution
- [ ] describe_solution_version
- [ ] get_solution_metrics
- [ ] list_batch_inference_jobs
- [ ] list_batch_segment_jobs
- [ ] list_campaigns
- [ ] list_dataset_export_jobs
- [ ] list_dataset_groups
- [ ] list_dataset_import_jobs
- [ ] list_datasets
- [ ] list_event_trackers
- [ ] list_filters
- [ ] list_recipes
- [ ] list_recommenders
- [X] list_schemas
- [ ] list_solution_versions
- [ ] list_solutions
- [ ] list_tags_for_resource
- [ ] start_recommender
- [ ] stop_recommender
- [ ] stop_solution_version_creation
- [ ] tag_resource
- [ ] untag_resource
- [ ] update_campaign
- [ ] update_recommender
</details>
## pinpoint
<details>
<summary>10% implemented</summary>
@ -6268,7 +6334,6 @@
- opsworkscm
- outposts
- panorama
- personalize
- personalize-events
- personalize-runtime
- pi

View File

@ -0,0 +1,94 @@
.. _implementedservice_personalize:
.. |start-h3| raw:: html
<h3>
.. |end-h3| raw:: html
</h3>
===========
personalize
===========
.. autoclass:: moto.personalize.models.PersonalizeBackend
|start-h3| Example usage |end-h3|
.. sourcecode:: python
@mock_personalize
def test_personalize_behaviour:
boto3.client("personalize")
...
|start-h3| Implemented features for this service |end-h3|
- [ ] create_batch_inference_job
- [ ] create_batch_segment_job
- [ ] create_campaign
- [ ] create_dataset
- [ ] create_dataset_export_job
- [ ] create_dataset_group
- [ ] create_dataset_import_job
- [ ] create_event_tracker
- [ ] create_filter
- [ ] create_recommender
- [X] create_schema
- [ ] create_solution
- [ ] create_solution_version
- [ ] delete_campaign
- [ ] delete_dataset
- [ ] delete_dataset_group
- [ ] delete_event_tracker
- [ ] delete_filter
- [ ] delete_recommender
- [X] delete_schema
- [ ] delete_solution
- [ ] describe_algorithm
- [ ] describe_batch_inference_job
- [ ] describe_batch_segment_job
- [ ] describe_campaign
- [ ] describe_dataset
- [ ] describe_dataset_export_job
- [ ] describe_dataset_group
- [ ] describe_dataset_import_job
- [ ] describe_event_tracker
- [ ] describe_feature_transformation
- [ ] describe_filter
- [ ] describe_recipe
- [ ] describe_recommender
- [X] describe_schema
- [ ] describe_solution
- [ ] describe_solution_version
- [ ] get_solution_metrics
- [ ] list_batch_inference_jobs
- [ ] list_batch_segment_jobs
- [ ] list_campaigns
- [ ] list_dataset_export_jobs
- [ ] list_dataset_groups
- [ ] list_dataset_import_jobs
- [ ] list_datasets
- [ ] list_event_trackers
- [ ] list_filters
- [ ] list_recipes
- [ ] list_recommenders
- [X] list_schemas
Pagination is not yet implemented
- [ ] list_solution_versions
- [ ] list_solutions
- [ ] list_tags_for_resource
- [ ] start_recommender
- [ ] stop_recommender
- [ ] stop_solution_version_creation
- [ ] tag_resource
- [ ] untag_resource
- [ ] update_campaign
- [ ] update_recommender

View File

@ -107,6 +107,7 @@ mock_mediastoredata = lazy_load(
mock_mq = lazy_load(".mq", "mock_mq", boto3_name="mq")
mock_opsworks = lazy_load(".opsworks", "mock_opsworks")
mock_organizations = lazy_load(".organizations", "mock_organizations")
mock_personalize = lazy_load(".personalize", "mock_personalize")
mock_pinpoint = lazy_load(".pinpoint", "mock_pinpoint")
mock_polly = lazy_load(".polly", "mock_polly")
mock_quicksight = lazy_load(".quicksight", "mock_quicksight")

View File

@ -103,6 +103,7 @@ backend_url_patterns = [
("mq", re.compile("https?://mq\\.(.+)\\.amazonaws\\.com")),
("opsworks", re.compile("https?://opsworks\\.us-east-1\\.amazonaws.com")),
("organizations", re.compile("https?://organizations\\.(.+)\\.amazonaws\\.com")),
("personalize", re.compile("https?://personalize\\.(.+)\\.amazonaws\\.com")),
("pinpoint", re.compile("https?://pinpoint\\.(.+)\\.amazonaws\\.com")),
("polly", re.compile("https?://polly\\.(.+)\\.amazonaws.com")),
("quicksight", re.compile("https?://quicksight\\.(.+)\\.amazonaws\\.com")),

View File

@ -0,0 +1,5 @@
"""personalize module initialization; sets value for base decorator."""
from .models import personalize_backends
from ..core.models import base_decorator
mock_personalize = base_decorator(personalize_backends)

View File

@ -0,0 +1,13 @@
"""Exceptions raised by the personalize service."""
from moto.core.exceptions import JsonRESTError
class PersonalizeException(JsonRESTError):
pass
class ResourceNotFoundException(PersonalizeException):
def __init__(self, arn):
super().__init__(
"ResourceNotFoundException", f"Resource Arn {arn} does not exist."
)

View File

@ -0,0 +1,64 @@
"""PersonalizeBackend class with methods for supported APIs."""
from .exceptions import ResourceNotFoundException
from moto.core import BaseBackend, BaseModel
from moto.core.utils import BackendDict, unix_time
class Schema(BaseModel):
def __init__(self, account_id, region, name, schema, domain):
self.name = name
self.schema = schema
self.domain = domain
self.arn = f"arn:aws:personalize:{region}:{account_id}:schema/{name}"
self.created = unix_time()
def to_dict(self, full=True):
d = {
"name": self.name,
"schemaArn": self.arn,
"domain": self.domain,
"creationDateTime": self.created,
"lastUpdatedDateTime": self.created,
}
if full:
d["schema"] = self.schema
return d
class PersonalizeBackend(BaseBackend):
"""Implementation of Personalize APIs."""
def __init__(self, region_name, account_id):
super().__init__(region_name, account_id)
self.schemas: [str, Schema] = dict()
def create_schema(self, name, schema, domain):
schema = Schema(
region=self.region_name,
account_id=self.account_id,
name=name,
schema=schema,
domain=domain,
)
self.schemas[schema.arn] = schema
return schema.arn
def delete_schema(self, schema_arn):
if schema_arn not in self.schemas:
raise ResourceNotFoundException(schema_arn)
self.schemas.pop(schema_arn, None)
def describe_schema(self, schema_arn):
if schema_arn not in self.schemas:
raise ResourceNotFoundException(schema_arn)
return self.schemas[schema_arn]
def list_schemas(self) -> [Schema]:
"""
Pagination is not yet implemented
"""
return self.schemas.values()
personalize_backends = BackendDict(PersonalizeBackend, "personalize")

View File

@ -0,0 +1,48 @@
"""Handles incoming personalize requests, invokes methods, returns responses."""
import json
from moto.core.responses import BaseResponse
from .models import personalize_backends
class PersonalizeResponse(BaseResponse):
"""Handler for Personalize requests and responses."""
def __init__(self):
super().__init__(service_name="personalize")
@property
def personalize_backend(self):
"""Return backend instance specific for this region."""
return personalize_backends[self.current_account][self.region]
# add methods from here
def create_schema(self):
params = json.loads(self.body)
name = params.get("name")
schema = params.get("schema")
domain = params.get("domain")
schema_arn = self.personalize_backend.create_schema(
name=name,
schema=schema,
domain=domain,
)
return json.dumps(dict(schemaArn=schema_arn))
def delete_schema(self):
params = json.loads(self.body)
schema_arn = params.get("schemaArn")
self.personalize_backend.delete_schema(schema_arn=schema_arn)
return "{}"
def describe_schema(self):
params = json.loads(self.body)
schema_arn = params.get("schemaArn")
schema = self.personalize_backend.describe_schema(schema_arn=schema_arn)
return json.dumps(dict(schema=schema.to_dict()))
def list_schemas(self):
schemas = self.personalize_backend.list_schemas()
resp = {"schemas": [s.to_dict(full=False) for s in schemas]}
return json.dumps(resp)

11
moto/personalize/urls.py Normal file
View File

@ -0,0 +1,11 @@
"""personalize base URL and path."""
from .responses import PersonalizeResponse
url_bases = [
r"https?://personalize\.(.+)\.amazonaws\.com",
]
url_paths = {
"{0}/$": PersonalizeResponse.dispatch,
}

View File

View File

@ -0,0 +1,121 @@
"""Unit tests for personalize-supported APIs."""
import boto3
import json
import sure # noqa # pylint: disable=unused-import
import pytest
from botocore.exceptions import ClientError
from moto import mock_personalize
from moto.core import DEFAULT_ACCOUNT_ID
# See our Development Tips on writing tests for hints on how to write good tests:
# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
@mock_personalize
def test_create_schema():
client = boto3.client("personalize", region_name="ap-southeast-1")
schema = {
"type": "record",
"name": "Interactions",
"namespace": "com.amazonaws.personalize.schema",
"fields": [
{"name": "USER_ID", "type": "string"},
{"name": "ITEM_ID", "type": "string"},
{"name": "TIMESTAMP", "type": "long"},
],
"version": "1.0",
}
create_schema_response = client.create_schema(
name="personalize-demo-schema", schema=json.dumps(schema)
)
create_schema_response.should.have.key("schemaArn").equals(
f"arn:aws:personalize:ap-southeast-1:{DEFAULT_ACCOUNT_ID}:schema/personalize-demo-schema"
)
@mock_personalize
def test_delete_schema():
client = boto3.client("personalize", region_name="us-east-1")
schema_arn = client.create_schema(name="myname", schema=json.dumps("sth"))[
"schemaArn"
]
client.delete_schema(schemaArn=schema_arn)
client.list_schemas().should.have.key("schemas").equals([])
@mock_personalize
def test_delete_schema__unknown():
arn = f"arn:aws:personalize:ap-southeast-1:{DEFAULT_ACCOUNT_ID}:schema/personalize-demo-schema"
client = boto3.client("personalize", region_name="us-east-2")
with pytest.raises(ClientError) as exc:
client.delete_schema(schemaArn=arn)
err = exc.value.response["Error"]
err["Code"].should.equal("ResourceNotFoundException")
err["Message"].should.equal(f"Resource Arn {arn} does not exist.")
@mock_personalize
def test_describe_schema():
client = boto3.client("personalize", region_name="us-east-2")
schema_arn = client.create_schema(name="myname", schema="sth")["schemaArn"]
resp = client.describe_schema(schemaArn=schema_arn)
resp.should.have.key("schema")
schema = resp["schema"]
schema.should.have.key("name").equals("myname")
schema.should.have.key("schemaArn").match("schema/myname")
schema.should.have.key("schema").equals("sth")
schema.should.have.key("creationDateTime")
schema.should.have.key("lastUpdatedDateTime")
@mock_personalize
def test_describe_schema__with_domain():
client = boto3.client("personalize", region_name="us-east-2")
schema_arn = client.create_schema(name="myname", schema="sth", domain="ECOMMERCE")[
"schemaArn"
]
resp = client.describe_schema(schemaArn=schema_arn)
resp.should.have.key("schema")
schema = resp["schema"]
schema.should.have.key("domain").equals("ECOMMERCE")
@mock_personalize
def test_describe_schema__unknown():
arn = (
"arn:aws:personalize:ap-southeast-1:486285699788:schema/personalize-demo-schema"
)
client = boto3.client("personalize", region_name="us-east-2")
with pytest.raises(ClientError) as exc:
client.describe_schema(schemaArn=arn)
err = exc.value.response["Error"]
err["Code"].should.equal("ResourceNotFoundException")
err["Message"].should.equal(f"Resource Arn {arn} does not exist.")
@mock_personalize
def test_list_schemas__initial():
client = boto3.client("personalize", region_name="us-east-2")
resp = client.list_schemas()
resp.should.have.key("schemas").equals([])
@mock_personalize
def test_list_schema():
client = boto3.client("personalize", region_name="us-east-2")
schema_arn = client.create_schema(name="myname", schema="sth")["schemaArn"]
resp = client.list_schemas()
resp.should.have.key("schemas").length_of(1)
schema = resp["schemas"][0]
schema.should.have.key("name").equals("myname")
schema.should.have.key("schemaArn").equals(schema_arn)
schema.shouldnt.have.key("schema")
schema.should.have.key("creationDateTime")
schema.should.have.key("lastUpdatedDateTime")