From f1cc6eb7b6fb0c3814e67a103ef042c7eda80efb Mon Sep 17 00:00:00 2001 From: Geoff Weinhold Date: Tue, 17 Sep 2019 02:54:51 -0700 Subject: [PATCH] feat: add C# port of TypeScript 'my-widget-service' (#92) --- README.md | 18 + csharp/my-widget-service/.gitignore | 342 ++++++++++++++++++ csharp/my-widget-service/README.md | 39 ++ csharp/my-widget-service/cdk.json | 3 + .../my-widget-service/src/MyWidgetService.sln | 34 ++ .../MyWidgetService/MyWidgetService.csproj | 15 + .../MyWidgetService/MyWidgetServiceStack.cs | 50 +++ .../src/MyWidgetService/Program.cs | 17 + .../src/MyWidgetService/resources/widgets.js | 138 +++++++ 9 files changed, 656 insertions(+) create mode 100644 csharp/my-widget-service/.gitignore create mode 100644 csharp/my-widget-service/README.md create mode 100644 csharp/my-widget-service/cdk.json create mode 100644 csharp/my-widget-service/src/MyWidgetService.sln create mode 100644 csharp/my-widget-service/src/MyWidgetService/MyWidgetService.csproj create mode 100644 csharp/my-widget-service/src/MyWidgetService/MyWidgetServiceStack.cs create mode 100644 csharp/my-widget-service/src/MyWidgetService/Program.cs create mode 100644 csharp/my-widget-service/src/MyWidgetService/resources/widgets.js diff --git a/README.md b/README.md index 77c3e1888..26e00f4f9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Kit](https://github.com/awslabs/aws-cdk). Some examples are only available in Ty 2. [Java examples](#Java) 3. [Python examples](#Python) 4. [JavaScript examples](#JavaScript) +4. [.NET examples](#dotnet) ## TypeScript examples @@ -105,6 +106,23 @@ Select the following link to see how to install and run the example. |---------|-------------| | [aws-cdk-changelogs-demo](https://github.com/aws-samples/aws-cdk-changelogs-demo) | A full serverless Node.js application stack deployed using CDK. It uses AWS Lambda, AWS Fargate, DynamoDB, Elasticache, S3, and CloudFront. | +## .NET examples + +| Example | Description | +|---------|-------------| +| [my-widget-service](https://github.com/aws-samples/aws-cdk-examples/tree/master/csharp/my-widget-service/) | Use Lambda to serve up widgets | + +To run a .NET example, say my-widget-service: + +``` +$ npm install -g aws-cdk +$ cd csharp/my-widget-service +$ dotnet build src +$ cdk deploy // Deploys the CloudFormation template + +# Afterwards +$ cdk destroy +``` # License This library is licensed under the Apache 2.0 License. diff --git a/csharp/my-widget-service/.gitignore b/csharp/my-widget-service/.gitignore new file mode 100644 index 000000000..f555633ea --- /dev/null +++ b/csharp/my-widget-service/.gitignore @@ -0,0 +1,342 @@ +# CDK asset staging directory +.cdk.staging +cdk.out + +# Created by https://www.gitignore.io/api/csharp + +### Csharp ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + + +# End of https://www.gitignore.io/api/csharp \ No newline at end of file diff --git a/csharp/my-widget-service/README.md b/csharp/my-widget-service/README.md new file mode 100644 index 000000000..b5549e4b1 --- /dev/null +++ b/csharp/my-widget-service/README.md @@ -0,0 +1,39 @@ +This example uses lambda to serve-up widgets. + +## Build + +To build this app, you need to be in this example's root folder. Then run the following: + +```bash +npm install -g aws-cdk +dotnet build src +``` + +This will install the necessary CDK, then this example's dependencies, and then build your csharp files and your CloudFormation template. + +## Deploy + +Run `cdk deploy`. This will deploy / redeploy your Stack to your AWS Account. + +After the deployment you will see the API's URL, which represents the url you can then use. + +## Test the service +```bash +#List all widgets +curl https:///prod/123 +curl -X POST https:///prod/456 +curl -X POST https:///prod/abc + +#List all widgets (should return array) +curl https:///prod/123 +``` + +## Synthesize Cloudformation Template + +To see the Cloudformation template generated by the CDK, run `cdk synth`, then check the output file in the "cdk.out" directory. \ No newline at end of file diff --git a/csharp/my-widget-service/cdk.json b/csharp/my-widget-service/cdk.json new file mode 100644 index 000000000..00360290d --- /dev/null +++ b/csharp/my-widget-service/cdk.json @@ -0,0 +1,3 @@ +{ + "app": "dotnet src/MyWidgetService/bin/Debug/netcoreapp2.1/MyWidgetService.dll" +} diff --git a/csharp/my-widget-service/src/MyWidgetService.sln b/csharp/my-widget-service/src/MyWidgetService.sln new file mode 100644 index 000000000..0a39b0613 --- /dev/null +++ b/csharp/my-widget-service/src/MyWidgetService.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyWidgetService", "MyWidgetService\MyWidgetService.csproj", "{447E0A43-55B4-44AD-90E7-36758EC30F7D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {447E0A43-55B4-44AD-90E7-36758EC30F7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {447E0A43-55B4-44AD-90E7-36758EC30F7D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {447E0A43-55B4-44AD-90E7-36758EC30F7D}.Debug|x64.ActiveCfg = Debug|Any CPU + {447E0A43-55B4-44AD-90E7-36758EC30F7D}.Debug|x64.Build.0 = Debug|Any CPU + {447E0A43-55B4-44AD-90E7-36758EC30F7D}.Debug|x86.ActiveCfg = Debug|Any CPU + {447E0A43-55B4-44AD-90E7-36758EC30F7D}.Debug|x86.Build.0 = Debug|Any CPU + {447E0A43-55B4-44AD-90E7-36758EC30F7D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {447E0A43-55B4-44AD-90E7-36758EC30F7D}.Release|Any CPU.Build.0 = Release|Any CPU + {447E0A43-55B4-44AD-90E7-36758EC30F7D}.Release|x64.ActiveCfg = Release|Any CPU + {447E0A43-55B4-44AD-90E7-36758EC30F7D}.Release|x64.Build.0 = Release|Any CPU + {447E0A43-55B4-44AD-90E7-36758EC30F7D}.Release|x86.ActiveCfg = Release|Any CPU + {447E0A43-55B4-44AD-90E7-36758EC30F7D}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/csharp/my-widget-service/src/MyWidgetService/MyWidgetService.csproj b/csharp/my-widget-service/src/MyWidgetService/MyWidgetService.csproj new file mode 100644 index 000000000..664d5fee3 --- /dev/null +++ b/csharp/my-widget-service/src/MyWidgetService/MyWidgetService.csproj @@ -0,0 +1,15 @@ + + + + Exe + netcoreapp2.1 + + + + + + + + + + diff --git a/csharp/my-widget-service/src/MyWidgetService/MyWidgetServiceStack.cs b/csharp/my-widget-service/src/MyWidgetService/MyWidgetServiceStack.cs new file mode 100644 index 000000000..1d14efb2d --- /dev/null +++ b/csharp/my-widget-service/src/MyWidgetService/MyWidgetServiceStack.cs @@ -0,0 +1,50 @@ +using Amazon.CDK; +using Amazon.CDK.AWS.APIGateway; +using Amazon.CDK.AWS.Lambda; +using Amazon.CDK.AWS.S3; +using System.Collections.Generic; + +namespace MyWidgetService +{ + public class MyWidgetServiceStack : Stack + { + public MyWidgetServiceStack(Construct parent, string id, IStackProps props) : base(parent, id, props) + { + var bucket = new Bucket(this, "WidgetStore", null); + + var handler = new Function(this, "WidgetHandler", new FunctionProps { + Runtime = Runtime.NODEJS_8_10, + Code = AssetCode.Asset("src/MyWidgetService/resources"), + Handler = "widgets.main", + Environment = new Dictionary{ + { "BUCKET", bucket.BucketName } + } + }); + + bucket.GrantReadWrite(handler, null); + + var api = new RestApi(this, "widgets-api", new RestApiProps { + RestApiName = "Widget Service", + Description = "This service serves widgets" + }); + + var getWidgetsIntegration = new LambdaIntegration(handler, new LambdaIntegrationOptions{ + RequestTemplates = new Dictionary{ + { "application/json", "{ 'statusCode', '200'}" } + }, + }); + + api.Root.AddMethod("GET", getWidgetsIntegration, null); + + var widget = api.Root.AddResource("{id}", null); + + var postWidgetIntegration = new LambdaIntegration(handler, null); + var getWidgetIntegration = new LambdaIntegration(handler, null); + var deleteWidgetIntegration = new LambdaIntegration(handler, null); + + widget.AddMethod("POST", postWidgetIntegration, null); + widget.AddMethod("GET", getWidgetIntegration, null); + widget.AddMethod("DELETE", deleteWidgetIntegration, null); + } + } +} diff --git a/csharp/my-widget-service/src/MyWidgetService/Program.cs b/csharp/my-widget-service/src/MyWidgetService/Program.cs new file mode 100644 index 000000000..f6e028051 --- /dev/null +++ b/csharp/my-widget-service/src/MyWidgetService/Program.cs @@ -0,0 +1,17 @@ +using Amazon.CDK; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MyWidgetService +{ + class Program + { + static void Main(string[] args) + { + var app = new App(null); + new MyWidgetServiceStack(app, "MyWidgetServiceStack", new StackProps()); + app.Synth(); + } + } +} diff --git a/csharp/my-widget-service/src/MyWidgetService/resources/widgets.js b/csharp/my-widget-service/src/MyWidgetService/resources/widgets.js new file mode 100644 index 000000000..c5c9f2a1d --- /dev/null +++ b/csharp/my-widget-service/src/MyWidgetService/resources/widgets.js @@ -0,0 +1,138 @@ +//snippet-comment:[These are tags for the AWS doc team's sample catalog. Do not remove.] +//snippet-comment:[This should be in the resources/ directory] +//snippet-comment:[and only works with my_widget_service.ts in the bin/ directory] +//snippet-comment:[and widget_service.ts in the lib/ directory.] +//snippet-sourceauthor:[Doug-AWS] +//snippet-sourcedescription:[Lambda function to handle GET, POST, and DELETE.] +//snippet-keyword:[CDK V0.24.1] +//snippet-keyword:[S3.deleteObject function] +//snippet-keyword:[S3.getObject function] +//snippet-keyword:[S3.listObjectsV2 function] +//snippet-keyword:[S3.putObject function] +//snippet-keyword:[JavaScript] +//snippet-service:[cdk] +//snippet-sourcetype:[full-example] +//snippet-sourcedate:[2019-2-8] +// Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// This file is licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +// OF ANY KIND, either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +//snippet-start:[cdk.typescript.widgets] +//snippet-start:[cdk.typescript.widgets.imports] +const AWS = require('aws-sdk'); +const S3 = new AWS.S3(); +//snippet-end:[cdk.typescript.widgets.imports] + +const bucketName = process.env.BUCKET; + +//snippet-start:[cdk.typescript.widgets.exports_main] +exports.main = async function(event, context) { + try { + var method = event.httpMethod; + // Get name, if present + var widgetName = event.path.startsWith('/') ? event.path.substring(1) : event.path; + + if (method === "GET") { + // GET / to get the names of all widgets + if (event.path === "/") { + const data = await S3.listObjectsV2({ Bucket: bucketName }).promise(); + var body = { + widgets: data.Contents.map(function(e) { return e.Key }) + }; + return { + statusCode: 200, + headers: {}, + body: JSON.stringify(body) + }; + } + + if (widgetName) { + // GET /name to get info on widget name + const data = await S3.getObject({ Bucket: bucketName, Key: widgetName}).promise(); + var body = data.Body.toString('utf-8'); + + return { + statusCode: 200, + headers: {}, + body: JSON.stringify(body) + }; + } + } + + if (method === "POST") { + // POST /name + // Return error if we do not have a name + if (!widgetName) { + return { + statusCode: 400, + headers: {}, + body: "Widget name missing" + }; + } + + // Create some dummy data to populate object + const now = new Date(); + var data = widgetName + " created: " + now; + + var base64data = new Buffer(data, 'binary'); + + await S3.putObject({ + Bucket: bucketName, + Key: widgetName, + Body: base64data, + ContentType: 'application/json' + }).promise(); + + return { + statusCode: 200, + headers: {}, + body: JSON.stringify(event.widgets) + }; + } + + if (method === "DELETE") { + // DELETE /name + // Return an error if we do not have a name + if (!widgetName) { + return { + statusCode: 400, + headers: {}, + body: "Widget name missing" + }; + } + + await S3.deleteObject({ + Bucket: bucketName, Key: widgetName + }).promise(); + + return { + statusCode: 200, + headers: {}, + body: "Successfully deleted widget " + widgetName + }; + } + + // We got something besides a GET, POST, or DELETE + return { + statusCode: 400, + headers: {}, + body: "We only accept GET, POST, and DELETE, not " + method + }; + } catch(error) { + var body = error.stack || JSON.stringify(error, null, 2); + return { + statusCode: 400, + headers: {}, + body: body + } + } +} +//snippet-end:[cdk.typescript.widgets.exports_main] +//snippet-end:[cdk.typescript.widgets] \ No newline at end of file