Add Rekognition support. (#4957)

This commit is contained in:
CoderDonohoe 2022-03-29 14:15:25 +01:00 committed by GitHub
parent a271057df2
commit 4ed5a837de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 325 additions and 0 deletions

View File

@ -131,6 +131,9 @@ mock_redshift = lazy_load(".redshift", "mock_redshift")
mock_redshiftdata = lazy_load(
".redshiftdata", "mock_redshiftdata", boto3_name="redshift-data"
)
mock_rekognition = lazy_load(
".rekognition", "mock_rekognition", boto3_name="rekognition"
)
mock_resourcegroups = lazy_load(
".resourcegroups", "mock_resourcegroups", boto3_name="resource-groups"
)

View File

@ -102,6 +102,7 @@ backend_url_patterns = [
("rds", re.compile("https?://rds\\.amazonaws\\.com")),
("redshift", re.compile("https?://redshift\\.(.+)\\.amazonaws\\.com")),
("redshift-data", re.compile("https?://redshift-data\\.(.+)\\.amazonaws\\.com")),
("rekognition", re.compile("https?://rekognition\\.(.+)\\.amazonaws\\.com")),
(
"resource-groups",
re.compile("https?://resource-groups(-fips)?\\.(.+)\\.amazonaws.com"),

View File

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

View File

@ -0,0 +1 @@
"""Exceptions raised by the rekognition service."""

215
moto/rekognition/models.py Normal file
View File

@ -0,0 +1,215 @@
"""RekognitionBackend class with methods for supported APIs."""
import random
import string
from moto.core import BaseBackend
from moto.core.utils import BackendDict
class RekognitionBackend(BaseBackend):
"""Implementation of Rekognition APIs."""
def __init__(self, region_name=None):
self.region_name = region_name
def reset(self):
"""Re-initialize all attributes for this instance."""
region_name = self.region_name
self.__dict__ = {}
self.__init__(region_name)
def start_text_detection(self):
return self._job_id()
def get_text_detection(self):
"""
This returns hardcoded values and none of the parameters are taken into account.
"""
return (
self._job_status(),
self._status_message(),
self._video_metadata(),
self._text_detections(),
self._next_token(),
self._text_model_version(),
)
# private
def _job_id(self):
return "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(64)
)
def _job_status(self):
return "SUCCEEDED"
def _next_token(self):
return ""
def _status_message(self):
return ""
def _text_model_version(self):
return "3.1"
def _video_metadata(self):
return {
"Codec": "h264",
"DurationMillis": 15020,
"Format": "QuickTime / MOV",
"FrameRate": 24.0,
"FrameHeight": 720,
"FrameWidth": 1280,
"ColorRange": "LIMITED",
}
def _text_detections(self):
return [
{
"Timestamp": 0,
"TextDetection": {
"DetectedText": "Hello world",
"Type": "LINE",
"Id": 0,
"Confidence": 97.89398956298828,
"Geometry": {
"BoundingBox": {
"Width": 0.1364741027355194,
"Height": 0.0318513885140419,
"Left": 0.4310702085494995,
"Top": 0.876121461391449,
},
"Polygon": [
{"X": 0.4310702085494995, "Y": 0.8769540190696716},
{"X": 0.5673548579216003, "Y": 0.876121461391449},
{"X": 0.5675443410873413, "Y": 0.90714031457901},
{"X": 0.4312596917152405, "Y": 0.9079728722572327},
],
},
},
},
{
"Timestamp": 0,
"TextDetection": {
"DetectedText": "Hello",
"Type": "WORD",
"Id": 1,
"ParentId": 0,
"Confidence": 99.1568832397461,
"Geometry": {
"BoundingBox": {
"Width": 0.0648193359375,
"Height": 0.0234375,
"Left": 0.43121337890625,
"Top": 0.876953125,
},
"Polygon": [
{"X": 0.43121337890625, "Y": 0.876953125},
{"X": 0.49603271484375, "Y": 0.876953125},
{"X": 0.49603271484375, "Y": 0.900390625},
{"X": 0.43121337890625, "Y": 0.900390625},
],
},
},
},
{
"Timestamp": 0,
"TextDetection": {
"DetectedText": "world",
"Type": "WORD",
"Id": 2,
"ParentId": 0,
"Confidence": 96.63108825683594,
"Geometry": {
"BoundingBox": {
"Width": 0.07103776931762695,
"Height": 0.02804870530962944,
"Left": 0.4965003430843353,
"Top": 0.8795245885848999,
},
"Polygon": [
{"X": 0.4965003430843353, "Y": 0.8809727430343628},
{"X": 0.5673661231994629, "Y": 0.8795245885848999},
{"X": 0.5675381422042847, "Y": 0.9061251282691956},
{"X": 0.4966723322868347, "Y": 0.9075732827186584},
],
},
},
},
{
"Timestamp": 1000,
"TextDetection": {
"DetectedText": "Goodbye world",
"Type": "LINE",
"Id": 0,
"Confidence": 98.9729995727539,
"Geometry": {
"BoundingBox": {
"Width": 0.13677978515625,
"Height": 0.0302734375,
"Left": 0.43121337890625,
"Top": 0.876953125,
},
"Polygon": [
{"X": 0.43121337890625, "Y": 0.876953125},
{"X": 0.5679931640625, "Y": 0.876953125},
{"X": 0.5679931640625, "Y": 0.9072265625},
{"X": 0.43121337890625, "Y": 0.9072265625},
],
},
},
},
{
"Timestamp": 1000,
"TextDetection": {
"DetectedText": "Goodbye",
"Type": "WORD",
"Id": 1,
"ParentId": 0,
"Confidence": 99.7258529663086,
"Geometry": {
"BoundingBox": {
"Width": 0.0648193359375,
"Height": 0.0234375,
"Left": 0.43121337890625,
"Top": 0.876953125,
},
"Polygon": [
{"X": 0.43121337890625, "Y": 0.876953125},
{"X": 0.49603271484375, "Y": 0.876953125},
{"X": 0.49603271484375, "Y": 0.900390625},
{"X": 0.43121337890625, "Y": 0.900390625},
],
},
},
},
{
"Timestamp": 1000,
"TextDetection": {
"DetectedText": "world",
"Type": "WORD",
"Id": 2,
"ParentId": 0,
"Confidence": 98.22015380859375,
"Geometry": {
"BoundingBox": {
"Width": 0.0703125,
"Height": 0.0263671875,
"Left": 0.4976806640625,
"Top": 0.880859375,
},
"Polygon": [
{"X": 0.4976806640625, "Y": 0.880859375},
{"X": 0.5679931640625, "Y": 0.880859375},
{"X": 0.5679931640625, "Y": 0.9072265625},
{"X": 0.4976806640625, "Y": 0.9072265625},
],
},
},
},
]
rekognition_backends = BackendDict(RekognitionBackend, "rekognition")

View File

@ -0,0 +1,45 @@
"""Handles incoming rekognition requests, invokes methods, returns responses."""
import json
from moto.core.responses import BaseResponse
from .models import rekognition_backends
class RekognitionResponse(BaseResponse):
"""Handler for Rekognition requests and responses."""
@property
def rekognition_backend(self):
"""Return backend instance specific for this region."""
return rekognition_backends[self.region]
def get_text_detection(self):
(
job_status,
status_message,
video_metadata,
text_detections,
next_token,
text_model_version,
) = self.rekognition_backend.get_text_detection()
return json.dumps(
dict(
JobStatus=job_status,
StatusMessage=status_message,
VideoMetadata=video_metadata,
TextDetections=text_detections,
NextToken=next_token,
TextModelVersion=text_model_version,
)
)
def start_text_detection(self):
headers = {"Content-Type": "application/x-amz-json-1.1"}
job_id = self.rekognition_backend.start_text_detection()
response = ('{"JobId":"' + job_id + '"}').encode()
return 200, headers, response
# add templates from here

10
moto/rekognition/urls.py Normal file
View File

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

View File

View File

@ -0,0 +1,45 @@
"""Unit tests for rekognition-supported APIs."""
import random
import string
import boto3
import sure # noqa # pylint: disable=unused-import
from moto import mock_rekognition
# 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_rekognition
def test_start_text_detection():
client = boto3.client("rekognition", region_name="ap-southeast-1")
video = {
"S3Object": {
"Bucket": "bucket",
"Name": "key",
}
}
resp = client.start_text_detection(Video=video)
resp["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
resp.should.have.key("JobId")
@mock_rekognition
def test_get_text_detection():
client = boto3.client("rekognition", region_name="us-east-2")
job_id = "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(64)
)
resp = client.get_text_detection(JobId=job_id)
resp["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
resp["JobStatus"].should.equal("SUCCEEDED")
resp["TextDetections"][0]["TextDetection"]["DetectedText"].should.equal(
"Hello world"
)