Mercurial > public > tweet-analysis
changeset 5:54e71cf6e324
refactor code
author | Dennis Concepcion Martin <dennisconcepcionmartin@gmail.com> |
---|---|
date | Fri, 17 Sep 2021 17:42:30 +0200 |
parents | cfd876570008 |
children | db2ce7097ff3 |
files | README.md dependencies/__init__.py dependencies/python/__init__.py dependencies/python/event_controller.py dependencies/python/requirements.txt dependencies/python/secrets_controller.py dependencies/python/url_controller.py events/event.json src/__init__.py src/events/sentiment_event.json src/requirements.txt template.yaml |
diffstat | 9 files changed, 121 insertions(+), 71 deletions(-) [+] |
line wrap: on
line diff
--- a/README.md Thu Sep 16 18:03:26 2021 +0200 +++ b/README.md Fri Sep 17 17:42:30 2021 +0200 @@ -80,7 +80,7 @@ Run functions locally and invoke them with the `sam local invoke` command. ```bash -tweet-analysis$ sam local invoke HelloWorldFunction --event events/event.json +tweet-analysis$ sam local invoke HelloWorldFunction --event events/sentiment_event.json ``` The SAM CLI can also emulate your application's API. Use the `sam local start-api` to run the API locally on port 3000.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dependencies/python/event_controller.py Fri Sep 17 17:42:30 2021 +0200 @@ -0,0 +1,24 @@ +def unwrap_sentiment_string_parameters(event): + """ + Unwrap string parameters from /sentiment api call + :param event: dict, required + API Gateway Lambda Proxy Input Format + :return: + """ + + twitter_user = 'Twitter' + number_of_tweets = '100' + + query_string_parameters = event['queryStringParameters'] + if event['queryStringParameters'] is not None: + if 'twitterUser' in query_string_parameters: + twitter_user = query_string_parameters['twitterUser'] + if not twitter_user: + twitter_user = 'Twitter' + + if 'numberOfTweets' in query_string_parameters: + number_of_tweets = query_string_parameters['numberOfTweets'] + if not number_of_tweets: + number_of_tweets = '100' + + return twitter_user, number_of_tweets
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dependencies/python/requirements.txt Fri Sep 17 17:42:30 2021 +0200 @@ -0,0 +1,1 @@ +boto3 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dependencies/python/secrets_controller.py Fri Sep 17 17:42:30 2021 +0200 @@ -0,0 +1,46 @@ +import boto3 +import base64 +import json +from botocore.exceptions import ClientError + + +def get_secret(secret_name, region_name="eu-west-2"): + """ + Get Secret Keys from AWS Secrets Manager + :return: + """ + + # Create a Secrets Manager client + session = boto3.session.Session() + client = session.client( + service_name='secretsmanager', + region_name=region_name + ) + + try: + get_secret_value_response = client.get_secret_value(SecretId=secret_name) + except ClientError as e: + if e.response['Error']['Code'] == 'DecryptionFailureException': + # Secrets Manager can't decrypt the protected secret text using the provided KMS key. + raise e + elif e.response['Error']['Code'] == 'InternalServiceErrorException': + # An error occurred on the server side. + raise e + elif e.response['Error']['Code'] == 'InvalidParameterException': + # You provided an invalid value for a parameter. + raise e + elif e.response['Error']['Code'] == 'InvalidRequestException': + # You provided a parameter value that is not valid for the current state of the resource. + raise e + elif e.response['Error']['Code'] == 'ResourceNotFoundException': + # AWS Can't find the resource that you asked for. + raise e + else: + # Decrypts secret using the associated KMS CMK. + # Depending on whether the secret is a string or binary, one of these fields will be populated. + if 'SecretString' in get_secret_value_response: + secret = get_secret_value_response['SecretString'] + else: + secret = base64.b64decode(get_secret_value_response['SecretBinary']) + + return json.loads(secret)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dependencies/python/url_controller.py Fri Sep 17 17:42:30 2021 +0200 @@ -0,0 +1,13 @@ +def create_twitter_url(twitter_user, number_of_tweets): + """ + Create url to fetch `max_results` of tweets from `user` + :param twitter_user: string, required + :param number_of_tweets: int, required + :return: string url + """ + + formatted_max_results = 'max_results={}'.format(number_of_tweets) + formatted_user = 'query=from:{}'.format(twitter_user) + url = "https://api.twitter.com/2/tweets/search/recent?{}&{}".format(formatted_max_results, formatted_user) + + return url
--- a/events/event.json Thu Sep 16 18:03:26 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -{ - "body": "{\"message\": \"hello world\"}", - "resource": "/sentiment", - "path": "/sentiment", - "httpMethod": "GET", - "isBase64Encoded": false, - "queryStringParameters": { - "foo": "bar" - }, - "pathParameters": { - "proxy": "/path/to/resource" - }, - "stageVariables": { - "baz": "qux" - }, - "headers": { - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", - "Accept-Encoding": "gzip, deflate, sdch", - "Accept-Language": "en-US,en;q=0.8", - "Cache-Control": "max-age=0", - "CloudFront-Forwarded-Proto": "https", - "CloudFront-Is-Desktop-Viewer": "true", - "CloudFront-Is-Mobile-Viewer": "false", - "CloudFront-Is-SmartTV-Viewer": "false", - "CloudFront-Is-Tablet-Viewer": "false", - "CloudFront-Viewer-Country": "US", - "Host": "1234567890.execute-api.us-east-1.amazonaws.com", - "Upgrade-Insecure-Requests": "1", - "User-Agent": "Custom User Agent String", - "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", - "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", - "X-Forwarded-For": "127.0.0.1, 127.0.0.2", - "X-Forwarded-Port": "443", - "X-Forwarded-Proto": "https" - }, - "requestContext": { - "accountId": "123456789012", - "resourceId": "123456", - "stage": "prod", - "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", - "requestTime": "09/Apr/2015:12:34:56 +0000", - "requestTimeEpoch": 1428582896000, - "identity": { - "cognitoIdentityPoolId": null, - "accountId": null, - "cognitoIdentityId": null, - "caller": null, - "accessKey": null, - "sourceIp": "127.0.0.1", - "cognitoAuthenticationType": null, - "cognitoAuthenticationProvider": null, - "userArn": null, - "userAgent": "Custom User Agent String", - "user": null - }, - "path": "/prod/hello", - "resourcePath": "/hello", - "httpMethod": "POST", - "apiId": "1234567890", - "protocol": "HTTP/1.1" - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/events/sentiment_event.json Fri Sep 17 17:42:30 2021 +0200 @@ -0,0 +1,10 @@ +{ + "resource": "/sentiment", + "path": "/sentiment", + "httpMethod": "GET", + "isBase64Encoded": false, + "queryStringParameters": { + "twitterUser": "dennisconcep", + "numberOfTweets": "50" + } +}
--- a/src/requirements.txt Thu Sep 16 18:03:26 2021 +0200 +++ b/src/requirements.txt Fri Sep 17 17:42:30 2021 +0200 @@ -1,2 +1,1 @@ -requests -boto3 \ No newline at end of file +requests \ No newline at end of file
--- a/template.yaml Thu Sep 16 18:03:26 2021 +0200 +++ b/template.yaml Fri Sep 17 17:42:30 2021 +0200 @@ -13,6 +13,7 @@ ApiKeyRequired: true Resources: + ## ### START API GATEWAY CONFIGURATION ### ## @@ -38,7 +39,7 @@ Description: Deployment of Api version 1 RestApiId: !Ref ServerlessRestApi - # Create usage plan + # Create PaidUsagePlan usage plan PaidUsagePlan: Type: AWS::ApiGateway::UsagePlan Properties: @@ -59,7 +60,7 @@ - Key: "Name" Value: "tweet-analysis::rest-api::paid-usage-plan" - # Create Api key + # Create Api key for PaidUsagePlan PaidApiKey: Type: AWS::ApiGateway::ApiKey Properties: @@ -119,21 +120,22 @@ - Key: "application-id" Value: "tweet-analysis" - Key: "Name" - Value: "tweet-analysis::certificate::dennistech.io" + Value: "tweet-analysis::domain-certificate::dennistech.io" - # Map domain to the regional domain generated by Api Gateway + # Map tweet-analysis.dennistech.io to the regional domain generated by Api Gateway DomainMapping: Type: AWS::Route53::RecordSet Properties: + Name: tweet-analysis.dennistech.io + Comment: Map domain to the regional domain generated by Api Gateway HostedZoneId: Z0937998E3C5GEK4NHO9 - Name: tweet-analysis.dennistech.io Type: A AliasTarget: DNSName: !GetAtt Domain.RegionalDomainName EvaluateTargetHealth: true HostedZoneId: !GetAtt Domain.RegionalHostedZoneId - # Map paths from your domain name to your API stages + # Configure mapping in API Gateway custom domain to point to v1 stage PathMapping: Type: AWS::ApiGateway::BasePathMapping Properties: @@ -153,6 +155,7 @@ GetTweetSentimentFunction: Type: AWS::Serverless::Function Properties: + FunctionName: GetTweetSentimentFunction Description: Fetch tweets and analyse sentiment using AWS Comprehend CodeUri: src/ Handler: handlers/sentiment.get_tweet_sentiment @@ -161,15 +164,31 @@ - AWSSecretsManagerGetSecretValuePolicy: SecretArn: arn:aws:secretsmanager:eu-west-2:339008578167:secret:tweet-analysis-keys-gKo6DQ + Layers: + - !Ref TweetAnalysisSharedFunctions Events: CallGetTweetSentiment: Type: Api Properties: Path: /sentiment Method: get + RequestParameters: + - method.request.querystring.twitterUser: + Required: false + - method.request.querystring.numberOfTweets: + Required: false Tags: Name: "tweet-analysis::get-tweet-sentiment-function" + # Define dependencies + TweetAnalysisSharedFunctions: + Type: AWS::Serverless::LayerVersion + Properties: + Description: Shared functions across lambda functions + CompatibleRuntimes: + - python3.9 + ContentUri: dependencies/ + ## ### END FUNCTION CONFIGURATION ### ##