From 3b234968666dd3151104d7d5624f2e332ebd75e0 Mon Sep 17 00:00:00 2001 From: Benny Bauer Date: Sun, 7 May 2017 09:52:43 +0300 Subject: [PATCH] fix issue #2 - support lambda-proxy changes - adhere to new api - add serverless-python-requirements plugin - add tests --- .gitignore | 118 ++++++++++++++++++++++++++--------------------- LICENSE | 21 +++++++++ README.md | 18 +------- event.json | 5 -- handler.py | 30 ++++++++---- package.json | 28 +++++++++++ requirements.txt | 3 +- serverless.yml | 58 ++--------------------- test.py | 30 ++++++++++++ 9 files changed, 173 insertions(+), 138 deletions(-) create mode 100644 LICENSE delete mode 100644 event.json create mode 100644 package.json create mode 100644 test.py diff --git a/.gitignore b/.gitignore index 22b6b1c..3c3a437 100644 --- a/.gitignore +++ b/.gitignore @@ -1,61 +1,66 @@ -# Created by https://www.gitignore.io/api/pycharm -### PyCharm ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 +# Created by https://www.gitignore.io/api/python,visualstudiocode,node,serverless -# User-specific stuff: -.idea/workspace.xml -.idea/tasks.xml -.idea/dictionaries -.idea/vcs.xml -.idea/jsLibraryMappings.xml +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt -# Sensitive or high-churn files: -.idea/dataSources.ids -.idea/dataSources.xml -.idea/dataSources.local.xml -.idea/sqlDataSources.xml -.idea/dynamic.xml -.idea/uiDesigner.xml +# Bower dependency directory (https://bower.io/) +bower_components -# Gradle: -.idea/gradle.xml -.idea/libraries +# node-waf configuration +.lock-wscript -# Mongo Explorer plugin: -.idea/mongoSettings.xml +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release -## File-based project format: -*.iws +# Dependency directories +node_modules/ +jspm_packages/ -## Plugin-specific files: +# Typescript v1 declaration files +typings/ -# IntelliJ -/out/ -.idea +# Optional npm cache directory +.npm -# mpeltonen/sbt-idea plugin -.idea_modules/ +# Optional eslint cache +.eslintcache -# JIRA plugin -atlassian-ide-plugin.xml +# Optional REPL history +.node_repl_history -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties +# Output of 'npm pack' +*.tgz -### PyCharm Patch ### -# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 +# Yarn Integrity file +.yarn-integrity -# *.iml -# modules.xml -# .idea/misc.xml -# *.ipr +# dotenv environment variables file +.env -# Created by https://www.gitignore.io/api/python ### Python ### # Byte-compiled / optimized / DLL files @@ -80,6 +85,7 @@ lib64/ parts/ sdist/ var/ +wheels/ *.egg-info/ .installed.cfg *.egg @@ -110,7 +116,6 @@ coverage.xml *.pot # Django stuff: -*.log local_settings.py # Flask stuff: @@ -126,7 +131,7 @@ docs/_build/ # PyBuilder target/ -# IPython Notebook +# Jupyter Notebook .ipynb_checkpoints # pyenv @@ -135,23 +140,32 @@ target/ # celery beat schedule file celerybeat-schedule +# SageMath parsed files +*.sage.py + # dotenv -.env # virtualenv -.venv/ +.venv venv/ ENV/ # Spyder project settings .spyderproject +.spyproject # Rope project settings .ropeproject -# Mac -.DS_Store +# mkdocs documentation +/site -# Serverless +### Serverless ### +# Ignore build directory .serverless -vendored +.requirements + +### VisualStudioCode ### +.vscode + +# End of https://www.gitignore.io/api/python,visualstudiocode,node,serverless diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2207619 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Benny Bauer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 9b766a8..0c3d0dc 100644 --- a/README.md +++ b/README.md @@ -26,24 +26,10 @@ The service has a dependency on external package (`requests`) and it exposes 2 R ## Deployment -Create dependencies folder for packaging - pip install -t vendored -r requirements.txt - -And... deploy! - sls deploy - + ### Invocation -You can trigger a function: - - # Invoke function without input - sls invoke -f listposts - - # Provide input - sls invoke -f getpost -p event.json - -or curl /posts curl /posts/5 @@ -79,4 +65,4 @@ Run a specific function with a provided input and get the logs sls invoke -f -p event.json -l # Credits -[JSONPlaceholder](https://jsonplaceholder.typicode.com) by [@typicode](https://github.com/typicode) is used for the posts backend. \ No newline at end of file +[JSONPlaceholder](https://jsonplaceholder.typicode.com) by [@typicode](https://github.com/typicode) is used for the posts backend. diff --git a/event.json b/event.json deleted file mode 100644 index a12ff4f..0000000 --- a/event.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "path": { - "id": "5" - } -} diff --git a/handler.py b/handler.py index 1dabe21..fe501f9 100644 --- a/handler.py +++ b/handler.py @@ -1,11 +1,5 @@ -# get this file's directory independent of where it's run from -import sys, os - -here = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(here, "./vendored")) - -import requests import logging +import requests log = logging.getLogger() log.setLevel(logging.DEBUG) @@ -14,14 +8,30 @@ def list_posts(event, context): + """ Retrieve posts list """ url = API_HOST + '/posts' log.debug('calling ' + url) - return requests.get(url).json() + res = requests.get(url) + + response = { + "statusCode": res.status_code, + "body": res.text + } + + return response def get_post(event, context): - url = API_HOST + '/posts/' + event['path']['id'] + """ Retrieve post by id """ + url = API_HOST + '/posts/' + str(event['pathParameters']['id']) log.debug('calling ' + url) - return requests.get(url).json() + res = requests.get(url) + + response = { + "statusCode": res.status_code, + "body": res.text + } + + return response diff --git a/package.json b/package.json new file mode 100644 index 0000000..5097124 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "serverless-python-sample", + "version": "1.0.1", + "description": "A simple serverless python sample", + "main": "index.js", + "scripts": { + "test": "python test.py" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/bennybauer/serverless-python-sample.git" + }, + "keywords": [ + "python", + "aws", + "lambda", + "serverless" + ], + "author": "bennybauer", + "license": "MIT", + "bugs": { + "url": "https://github.com/bennybauer/serverless-python-sample/issues" + }, + "homepage": "https://github.com/bennybauer/serverless-python-sample#readme", + "dependencies": { + "serverless-python-requirements": "^2.2.1" + } +} diff --git a/requirements.txt b/requirements.txt index 2420b3b..6f5275b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ -requests==2.11.1 -wheel==0.24.0 +requests==2.13.0 diff --git a/serverless.yml b/serverless.yml index ea20431..accc544 100644 --- a/serverless.yml +++ b/serverless.yml @@ -1,49 +1,13 @@ -# Welcome to Serverless! -# -# This file is the main config file for your service. -# It's very minimal at this point and uses default values. -# You can always add more config options for more control. -# We've included some commented out config examples here. -# Just uncomment any of them to get that config option. -# -# For full config options, check the docs: -# docs.serverless.com -# -# Happy Coding! - service: posts provider: name: aws runtime: python2.7 memorySize: 128 -# you can add statements to the Lambda function's IAM Role here -# iamRoleStatements: -# - Effect: "Allow" -# Action: -# - "s3:ListBucket" -# Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] } -# - Effect: "Allow" -# Action: -# - "s3:PutObject" -# Resource: -# Fn::Join: -# - "" -# - - "arn:aws:s3:::" -# - "Ref" : "ServerlessDeploymentBucket" - -# you can overwrite defaults here -#defaults: -# stage: dev -# region: us-east-1 -# you can add packaging information here -#package: -# include: -# - include-me.js -# exclude: -# - exclude-me.js -# artifact: my-service-code.zip +package: + exclude: + - test.py functions: listPosts: @@ -52,9 +16,6 @@ functions: - http: path: posts method: get -# - s3: ${env.BUCKET} -# - schedule: rate(10 minutes) -# - sns: greeter-topic getPost: handler: handler.get_post @@ -63,14 +24,5 @@ functions: path: posts/{id} method: get -# you can add CloudFormation resource templates here -#resources: -# Resources: -# NewResource: -# Type: AWS::S3::Bucket -# Properties: -# BucketName: my-new-bucket -# Outputs: -# NewOutput: -# Description: "Description for the output" -# Value: "Some output value +plugins: + - serverless-python-requirements diff --git a/test.py b/test.py new file mode 100644 index 0000000..c2acab5 --- /dev/null +++ b/test.py @@ -0,0 +1,30 @@ +import unittest +import json +from handler import list_posts, get_post + + +class TestHandler(unittest.TestCase): + """ Tests handler methods """ + + def test_list_posts(self): + """ Tests list_posts """ + res = list_posts(None, None) + self.assertEquals(200, res['statusCode']) + self.assertTrue(len(res['body']) > 0) + + def test_get_post(self): + """ Tests get_post """ + event = {} + event['pathParameters'] = {} + event['pathParameters']['id'] = "1" + res = get_post(event, None) + + self.assertEquals(200, res['statusCode']) + + post = json.loads(res['body']) + self.assertEquals(1, post['id']) + self.assertEquals("sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + post['title']) + +if __name__ == '__main__': + unittest.main()